FastAPI中如何优雅地处理表单校验错误?:这4种方案你必须掌握
2026/1/2 11:49:29
class TreeNode: def __init__(self, value): self.value = value # 节点存储的值 self.children = [] # 子节点列表 def add_child(self, child_node): self.children.append(child_node)该类支持动态添加子节点,适用于构建任意分支因子的树结构。from collections import deque def bfs_traverse(root): if not root: return queue = deque([root]) # 初始化队列 while queue: node = queue.popleft() # 取出队首节点 print(node.value) # 访问当前节点 queue.extend(node.children) # 将所有子节点加入队列| 遍历方式 | 适用场景 | 空间复杂度 |
|---|---|---|
| 深度优先(DFS) | 查找特定路径、解析嵌套结构 | O(h),h为树高 |
| 广度优先(BFS) | 寻找最短路径、层级分析 | O(w),w为最大宽度 |
class TreeNode: def __init__(self, val=0): self.val = val # 节点值 self.left = None # 左子节点引用 self.right = None # 右子节点引用上述代码定义了二叉树的基本节点结构。val存储数据,left和right分别指向左右子树,初始为None表示空树或叶节点。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 }上述代码中,切片模拟队列,queue[0]获取当前层节点,子节点按序加入尾部,保证层级顺序。循环持续至队列为空,完成整棵树的层序输出。func preorder(root *TreeNode) { if root == nil { return } fmt.Println(root.Val) // 访问根节点 preorder(root.Left) // 遍历左子树 preorder(root.Right) // 遍历右子树 }以上为前序遍历代码,若将打印语句移至左右递归之后,则变为后序遍历;若置于左递归之后、右递归之前,则为中序遍历。三者仅执行顺序之差,结构高度一致。Stack<TreeNode> stack = new Stack<>(); TreeNode curr = root; while (curr != null || !stack.isEmpty()) { while (curr != null) { stack.push(curr); // 保存路径 curr = curr.left; // 深入左子树 } curr = stack.pop(); // 弹出父节点 System.out.print(curr.val); // 访问根 curr = curr.right; // 转向右子树 }上述代码通过循环与栈配合,实现左-根-右的访问顺序。内层循环负责压入所有左子节点,外层循环处理弹出与右转逻辑。| 方式 | 空间开销 | 可控性 |
|---|---|---|
| 递归遍历 | O(h),隐式调用栈 | 低 |
| 栈模拟遍历 | O(h),显式栈 | 高 |
深度优先遍历(DFS)适用于需要探索所有路径或查找连通分量的场景,如树的前序、中序、后序遍历。
def dfs(node): if not node: return print(node.value) # 访问当前节点 dfs(node.left) # 递归遍历左子树 dfs(node.right) # 递归遍历右子树该实现通过递归深入左子树再右子树,适合结构化数据的路径探索。
广度优先遍历(BFS)常用于寻找最短路径或层级遍历,例如按层打印二叉树。
| 遍历方式 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| DFS | O(n) | O(h) | 路径探索、递归结构处理 |
| BFS | O(n) | O(w) | 最短路径、层级访问 |
其中 h 为树高,w 为最大宽度。
func preorderTraversal(root *TreeNode) []int { if root == nil { return nil } result := append([]int{}, root.Val) result = append(result, preorderTraversal(root.Left)...) result = append(result, preorderTraversal(root.Right)...) return result }该函数首先判断空节点作为递归终止条件;非空时将根节点值加入结果集,随后分别递归获取左右子树的遍历序列并拼接。// 中序遍历 void inorder(TreeNode* root) { if (!root) return; inorder(root->left); // 左 visit(root); // 根(此时处理) inorder(root->right); // 右 } // 后序遍历 void postorder(TreeNode* root) { if (!root) return; postorder(root->left); // 左 postorder(root->right); // 右 visit(root); // 根(延迟至最后) }上述代码显示:中序在递归左后立即处理当前节点,而后序将处理推迟到两个递归调用完成之后。def levelOrder(root): def dfs(node, depth, res): if not node: return if len(res) <= depth: res.append([]) res[depth].append(node.val) dfs(node.left, depth + 1, res) dfs(node.right, depth + 1, res) result = [] dfs(root, 0, result) return result上述代码通过depth参数控制当前所处层级,res存储每层的节点值。每次进入更深一层时,先检查结果列表是否已为该层分配空间。public void preorderTraversal(TreeNode root) { if (root == null) return; Stack<TreeNode> stack = new Stack<>(); stack.push(root); while (!stack.isEmpty()) { TreeNode node = stack.pop(); System.out.print(node.val + " "); // 访问节点 if (node.right != null) stack.push(node.right); // 右子入栈 if (node.left != null) stack.push(node.left); // 左子入栈 } }上述代码通过栈模拟系统调用过程。每次弹出当前节点并立即处理,随后按“右、左”顺序入栈,保证下一轮循环中左子树优先被访问,从而实现前序遍历逻辑。
public List<Integer> inorderTraversal(TreeNode root) { List<Integer> result = new ArrayList<>(); Stack<TreeNode> stack = new Stack<>(); TreeNode curr = root; while (curr != null || !stack.isEmpty()) { while (curr != null) { stack.push(curr); curr = curr.left; // 深入左子树 } curr = stack.pop(); result.add(curr.val); // 访问根节点 curr = curr.right; // 转向右子树 } return result; }上述代码通过循环和栈实现了对遍历状态的精准控制:内层循环负责“探底”至最左节点,外层循环则驱动整个访问流程向右推进,从而保证了左-根-右的访问次序。public List<Integer> postorderTraversal(TreeNode root) { if (root == null) return new ArrayList<>(); Stack<TreeNode> s1 = new Stack<>(); Stack<Integer> s2 = new Stack<>(); s1.push(root); while (!s1.isEmpty()) { TreeNode node = s1.pop(); s2.push(node.val); if (node.left != null) s1.push(node.left); if (node.right != null) s1.push(node.right); } List<Integer> res = new ArrayList<>(); while (!s2.isEmpty()) res.add(s2.pop()); return res; }该方法中,s1 按“根-右-左”压入节点,s2 存储反转路径,最终出栈顺序即为后序。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 }该实现中,切片模拟队列,每次处理当前层所有节点,确保按层级顺序输出。时间复杂度为 O(n),空间复杂度最坏为 O(w),w 为树的最大宽度。goroutine与通道的协作机制。例如,如何安全关闭带缓冲的 channel?以下是典型实现:ch := make(chan int, 10) done := make(chan bool) go func() { for value := range ch { fmt.Println("Received:", value) } done <- true }() ch <- 1 ch <- 2 close(ch) // 安全关闭,range 会自动退出 <-done| 参数 | 原配置 | 优化后 |
|---|---|---|
| 写入频率 | 每条立即写 | 每 10ms 批量刷盘 |
| I/O 模式 | 同步阻塞 | 双缓冲 + mmap |