1. 二叉树层序遍历实战解析
层序遍历(Level Order Traversal)是二叉树最基本的遍历方式之一,它按照树的层级从上到下、从左到右依次访问每个节点。这种遍历方式在实际开发中应用广泛,比如打印目录结构、计算网页DOM树的渲染顺序等场景。
1.1 BFS队列实现原理
BFS(广度优先搜索)是层序遍历的核心算法思想,其实现依赖于队列的先进先出特性。具体工作原理如下:
- 队列初始化:将根节点放入空队列中
- 循环处理:只要队列不为空,就重复以下步骤:
- 记录当前队列大小(即当前层的节点数)
- 依次取出当前层的所有节点进行处理
- 将每个节点的左右子节点(如果存在)加入队列
- 层级分离:通过队列大小的记录,确保每次只处理同一层的节点
关键点:在开始处理每一层之前,必须先获取队列的当前大小,这个size就是该层的节点总数。如果在循环中直接使用q.size()作为判断条件,会因为队列动态变化导致层级混乱。
1.2 代码实现与优化
基础实现如输入所示,这里分享几个优化技巧:
java复制// 优化点1:使用ArrayDeque代替LinkedList
Queue<TreeNode> q = new ArrayDeque<>(); // 性能更好
// 优化点2:提前分配结果列表容量(如果知道树的高度)
List<List<Integer>> res = new ArrayList<>(height);
// 优化点3:使用双端队列实现Z字形遍历
if (zigzag && level % 2 == 1) {
Collections.reverse(level); // 奇数层反转
}
在实际面试中,可能会遇到层序遍历的变种问题,比如:
- Z字形层序遍历(奇数层从左到右,偶数层从右到左)
- 只打印每层的最后一个节点(右视图问题)
- 计算每层的平均值
1.3 复杂度分析与应用场景
时间复杂度:O(n),每个节点恰好被访问一次
空间复杂度:O(w),w是树的最大宽度(即最宽那层的节点数)
层序遍历特别适合处理与树层级相关的问题,比如:
- 计算树的最小深度(第一个遇到的叶子节点所在层)
- 寻找每层的最大值
- 序列化/反序列化二叉树
2. 有序数组构建平衡二叉搜索树
2.1 分治算法设计思路
将有序数组转换为高度平衡的BST,关键在于理解BST的性质和分治思想:
- 平衡性要求:左右子树高度差不超过1
- BST性质:左子树所有节点 < 根节点 < 右子树所有节点
- 分治策略:
- 选择中间元素作为根节点
- 左半部分构建左子树
- 右半部分构建右子树
java复制private TreeNode build(int[] nums, int l, int r) {
if (l > r) return null;
int mid = l + (r - l) / 2; // 防溢出写法
TreeNode root = new TreeNode(nums[mid]);
root.left = build(nums, l, mid - 1);
root.right = build(nums, mid + 1, r);
return root;
}
2.2 边界条件处理要点
- 区间定义:使用闭区间[l, r]更直观
- 终止条件:l > r时返回null
- 中点计算:使用l + (r - l)/2避免整数溢出
- 元素处理:当l == r时,创建叶子节点
2.3 算法复杂度证明
时间复杂度:O(n),每个元素恰好被处理一次
空间复杂度:O(logn),递归栈的深度
这个算法生成的BST不唯一,因为当数组长度为偶数时,中间位置有两个候选。实际应用中,可以选择中间偏左或中间偏右作为根节点。
3. 验证二叉搜索树的两种经典方法
3.1 上下界约束法
这种方法通过传递当前节点的允许取值范围来验证BST:
java复制boolean check(TreeNode node, long lower, long upper) {
if (node == null) return true;
if (node.val <= lower || node.val >= upper) return false;
return check(node.left, lower, node.val)
&& check(node.right, node.val, upper);
}
注意事项:
- 初始上下界使用Long.MIN_VALUE和Long.MAX_VALUE
- 测试用例可能包含Integer边界值,所以不能用Integer
- 等于边界值的情况也视为无效(BST要求严格小于/大于)
3.2 中序遍历验证法
利用BST中序遍历必然升序的特性:
java复制class Solution {
private long prev = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
if (!isValidBST(root.left)) return false;
if (root.val <= prev) return false;
prev = root.val;
return isValidBST(root.right);
}
}
实现技巧:
- 使用成员变量保存前驱值
- 先递归验证左子树
- 检查当前节点是否大于前驱
- 更新前驱后验证右子树
常见错误:忘记处理等于的情况(BST要求严格递增)
4. 二叉搜索树中第K小元素
4.1 递归中序遍历解法
java复制class Solution {
private int count = 0;
private int result = 0;
public int kthSmallest(TreeNode root, int k) {
traverse(root, k);
return result;
}
private void traverse(TreeNode node, int k) {
if (node == null) return;
traverse(node.left, k);
count++;
if (count == k) {
result = node.val;
return;
}
traverse(node.right, k);
}
}
优化点:
- 找到结果后立即返回,避免不必要的遍历
- 对于频繁查询的场景,可以预处理建立节点到排名的映射
4.2 迭代中序遍历解法
java复制public int kthSmallest(TreeNode root, int k) {
Stack<TreeNode> stack = new Stack<>();
TreeNode curr = root;
int count = 0;
while (curr != null || !stack.isEmpty()) {
while (curr != null) {
stack.push(curr);
curr = curr.left;
}
curr = stack.pop();
if (++count == k) return curr.val;
curr = curr.right;
}
return -1;
}
优势:
- 不需要递归栈空间
- 找到目标后可立即返回
- 代码结构更适合处理大型树
5. 二叉树右视图的两种视角
5.1 BFS层序遍历法
java复制public List<Integer> rightSideView(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
if (i == size - 1) result.add(node.val);
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
}
return result;
}
特点:
- 直观易理解
- 适合各种二叉树结构
- 可以轻松扩展为左视图(i == 0时记录)
5.2 DFS优先右子树法
java复制public List<Integer> rightSideView(TreeNode root) {
List<Integer> result = new ArrayList<>();
dfs(root, 0, result);
return result;
}
private void dfs(TreeNode node, int depth, List<Integer> result) {
if (node == null) return;
if (depth == result.size()) result.add(node.val);
dfs(node.right, depth + 1, result);
dfs(node.left, depth + 1, result);
}
优势:
- 空间复杂度O(h),h为树高
- 代码更简洁
- 访问顺序可以灵活调整(先右后左得到右视图,先左后右得到左视图)
在实际应用中,如果树比较平衡,DFS方法通常更高效;而对于极端不平衡的树(如链表状),BFS方法可能更有优势。