3个理由告诉你,为什么ctfileGet正在重新定义网盘下载体验
2026/8/8 3:56:23
// Go语言实现中序遍历(递归) func inorder(root *TreeNode) { if root == nil { return } inorder(root.Left) // 遍历左子树 fmt.Println(root.Val) // 处理当前节点(访问时机决定遍历类型) inorder(root.Right) // 遍历右子树 }| 遍历方式 | 递归特点 | 迭代难点 |
|---|---|---|
| 前序 | 自然直观 | 需维护访问状态 |
| 中序 | 适用于BST有序输出 | 需模拟回溯路径 |
| 后序 | 适合释放资源场景 | 双栈或标记法复杂度高 |
def preorder(root): if root: print(root.val) # 访问根节点 preorder(root.left) # 遍历左子树 preorder(root.right) # 遍历右子树该递归代码清晰体现了前序遍历的执行流程:先处理当前节点数据,再依次深入左右子树。参数root表示当前子树根节点,通过空值判断实现递归终止。def inorder(root): if root: inorder(root.left) # 遍历左子树 print(root.val) # 访问根节点 inorder(root.right) # 遍历右子树该函数通过递归深入左子树,逐层回溯输出节点值。参数root表示当前子树根节点,None时终止递归。| 场景 | 是否适用中序遍历 |
|---|---|
| 获取BST排序数据 | 是 |
| 查找最大深度 | 否 |
func postOrder(root *TreeNode) { if root == nil { return } postOrder(root.Left) // 遍历左子树 postOrder(root.Right) // 遍历右子树 fmt.Println(root.Val) // 访问根节点 }该递归函数首先深入左、右子树完成所有子节点处理,最后输出当前节点值。参数root表示当前子树根节点,通过空指针判断实现递归终止。func factorial(n int) int { if n == 0 || n == 1 { return 1 } return n * factorial(n-1) // 调用自身 }该函数每次递归调用都将当前n值保存在栈帧中,直到n == 1触发回溯。例如factorial(4)的调用过程为:factorial(4) → factorial(3) → factorial(2) → factorial(1),随后逐层返回结果。| 调用层级 | n 值 | 栈帧状态 |
|---|---|---|
| 1 | 4 | 等待 factorial(3) 返回 |
| 2 | 3 | 等待 factorial(2) 返回 |
| 3 | 2 | 等待 factorial(1) 返回 |
| 4 | 1 | 返回 1 |
func evaluate(node *ExprNode) int { if node.isLeaf() { return node.value } left := evaluate(node.left) right := evaluate(node.right) switch node.op { case '+': return left + right case '*': return left * right } return 0 }该函数递归执行后序遍历,先计算子树结果,再应用操作符。参数node表示当前节点,叶节点存储数值,内部节点存储操作符。此策略符合运算优先级,天然支持嵌套表达式。type StackFrame struct { n int } func factorial(n int) int { stack := []StackFrame{} result := 1 stack = append(stack, StackFrame{n: n}) for len(stack) > 0 { frame := stack[len(stack)-1] stack = stack[:len(stack)-1] if frame.n == 0 || frame.n == 1 { continue } else { result *= frame.n stack = append(stack, StackFrame{n: frame.n - 1}) } } return result }上述代码将阶乘递归转换为迭代。每次将待处理的参数压栈,循环中弹出并更新结果。相比原递归,避免了深层调用栈带来的溢出风险,同时保留逻辑清晰性。func inorderTraversal(root *TreeNode) []int { var result []int var stack = [][2]interface{}{[2]interface{}{root, false}} for len(stack) > 0 { node, visited := stack[len(stack)-1] stack = stack[:len(stack)-1] if node == nil { continue } if visited { result = append(result, node.(*TreeNode).Val) } else { stack = append(stack, [2]interface{}{node.(*TreeNode).Right, false}, [2]interface{}{node, true}, [2]interface{}{node.(*TreeNode).Left, false}, ) } } return result }该代码适用于中序遍历;调整压栈顺序即可适配前序(中→左→右)或后序(左→右→中),实现三者统一框架。func ProcessLargeDataset(iter Iterator) { for iter.HasNext() { item := iter.Next() // 处理单个元素,无需缓存整体 process(item) } }上述代码中,Iterator接口封装了数据源的逐步访问逻辑。每次仅加载一个item,处理完成后即可被垃圾回收,极大缓解堆内存压力。| 方案 | 峰值内存 | 适用场景 |
|---|---|---|
| 批量加载 | 高 | 计算密集型 |
| 迭代处理 | 低 | 内存敏感型 |
type TreeNode struct { Val int Left *TreeNode Right *TreeNode } func levelOrder(root *TreeNode) []int { if root == nil { return nil } var result []int queue := []*TreeNode{root} for len(queue) > 0 { node := queue[0] queue = queue[1:] result = append(result, node.Val) if node.Left != nil { queue = append(queue, node.Left) } if node.Right != nil { queue = append(queue, node.Right) } } return result }上述代码通过切片模拟队列,每次取出首元素并将其子节点追加至尾部,确保层级顺序。参数 `root` 为二叉树根节点,返回值为按层序排列的节点值列表。func zigzagLevelOrder(root *TreeNode) [][]int { if root == nil { return nil } var result [][]int deque := list.New() deque.PushBack(root) leftToRight := true for deque.Len() > 0 { levelSize := deque.Len() levelNodes := make([]int, 0, levelSize) for i := 0; i < levelSize; i++ { if leftToRight { front := deque.Remove(deque.Front()).(*TreeNode) levelNodes = append(levelNodes, front.Val) if front.Left != nil { deque.PushBack(front.Left) } if front.Right != nil { deque.PushBack(front.Right) } } else { back := deque.Remove(deque.Back()).(*TreeNode) levelNodes = append(levelNodes, back.Val) if back.Right != nil { deque.PushFront(back.Right) } if back.Left != nil { deque.PushFront(back.Left) } } } result = append(result, levelNodes) leftToRight = !leftToRight } return result }上述代码通过双端队列动态调整节点进出方向,实现层级间反向输出。每次循环结束时翻转方向标志,确保下一层遍历方向正确切换。func boundaryTraversal(root *TreeNode) []int { if root == nil { return []int{} } var leftBoundary, leaves, rightBoundary []int // 前序收集左边界 collectLeftBoundary(root, &leftBoundary) // 后序收集右边界 collectRightBoundary(root, &rightBoundary) // 中序收集叶节点 collectLeaves(root, &leaves) // 合并结果,注意去重根节点和叶节点 return append(append(leftBoundary, leaves...), reverse(rightBoundary)...) }该函数通过三阶段遍历分别捕获边界元素。前序确保左边界自上而下,后序保障右边界自底向上,最终合并时需反转右边界数组以维持顺时针顺序。
map[int][]int存储列索引到节点值列表的映射,结合 DFS 遍历维护当前节点的行列坐标。func verticalOrder(root *TreeNode) [][]int { if root == nil { return [][]int{} } colMap := make(map[int][]int) queue := [][2]*TreeNode{{root, 0}} for len(queue) > 0 { node, col := queue[0][0], queue[0][1] queue = queue[1:] colMap[col] = append(colMap[col], node.Val) if node.Left != nil { queue = append(queue, [2]*TreeNode{node.Left, col - 1}) } if node.Right != nil { queue = append(queue, [2]*TreeNode{node.Right, col + 1}) } } // 按列索引排序输出 var cols []int for k := range colMap { cols = append(cols, k) } sort.Ints(cols) var result [][]int for _, c := range cols { result = append(result, colMap[c]) } return result }逻辑分析:利用 BFS 确保同一列中上方节点先被访问;哈希表动态收集各列节点,最后按列排序输出。
O(n log n),其中排序列索引占主导O(n),哈希表与队列存储所有节点在现代系统设计中,遍历不再局限于数组或链表的顺序访问。例如,在分布式文件系统中,遍历可能涉及跨节点的数据拉取与合并。以下 Go 代码展示了如何通过递归与并发结合的方式遍历一个模拟的分布式目录结构:
func traverseDistributedDir(nodes []string, path string, results chan<- string) { var wg sync.WaitGroup for _, node := range nodes { wg.Add(1) go func(n string) { defer wg.Done() // 模拟远程调用获取子路径 subPaths := fetchFromNode(n, path) for _, p := range subPaths { results <- p } }(node) } go func() { wg.Wait() close(results) }() }不同场景下应选择不同的遍历策略。下表总结了常见数据结构与对应的最优遍历方式:
| 数据结构 | 典型遍历方式 | 适用场景 |
|---|---|---|
| 二叉树 | 中序/后序递归 | 表达式求值、AST 解析 |
| 图(社交网络) | BFS + 剪枝 | 好友推荐、关系发现 |
| 嵌套 JSON | 栈式迭代 | API 响应解析、动态过滤 |