Leetcode 刷题记录 11 —— 二叉树第二弹
本系列为笔者的 Leetcode 刷题记录,顺序为 Hot 100 题官方顺序,根据标签命名,记录笔者总结的做题思路,附部分代码解释和疑问解答,01~07为C++语言,08及以后为Java语言。
01 二叉树的层序遍历
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public List<List<Integer>> levelOrder(TreeNode root) {//广度优先遍历 queue//1.创建queue集合,添加首节点Deque<TreeNode> queue = new LinkedList<>();queue.offer(root);//2.遍历tree,取出结点,判断结点情况List<List<Integer>> results = new ArrayList<>();if(root == null){return results;}while(!queue.isEmpty()){int size = queue.size();List<Integer> result = new ArrayList<>();for(int i=0; i<size; i++){TreeNode node = queue.poll();result.add(node.val);//3.添加左右孩子结点if(node.left != null){queue.offer(node.left);}if(node.right != null){queue.offer(node.right);}}results.add(result);}return results;}
}
02 将有序数组转换为二叉搜索树
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public TreeNode sortedArrayToBST(int[] nums) {}
}
给定二叉搜索树的中序遍历,是否可以唯一地确定二叉搜索树?答案是否定的。
如果增加一个限制条件,即要求二叉搜索树的高度平衡,是否可以唯一地确定二叉搜索树?答案仍然是否定的。
直观地看,我们可以选择中间数字作为二叉搜索树的根节点,这样分给左右子树的数字个数相同或只相差 1,可以使得树保持平衡。
方法一:递归
BST 是 Binary Search Tree(二叉搜索树)的缩写。
class Solution {public TreeNode sortedArrayToBST(int[] nums) {//1.构建递归函数,传递左右区间参数return helper(nums, 0, nums.length-1);}public TreeNode helper(int[] nums, int left, int right){if(left > right){ //特殊情况判断return null;}//2.添加当前root结点int mid = (left + right) / 2;TreeNode root = new TreeNode(nums[mid]);//3.递归添加左右孩子结点root.left = helper(nums, left, mid - 1);root.right = helper(nums, mid + 1, right);return root;}
}
03 验证二叉搜索树
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public boolean isValidBST(TreeNode root) {}
}
这启示我们设计一个递归函数 helper(root, lower, upper) 来递归判断,函数表示考虑以 root 为根的子树,判断子树中所有节点的值是否都在 (l,r) 的范围内(注意是开区间)。如果 root 节点的值 val 不在 (l,r) 的范围内说明不满足条件直接返回,否则我们要继续递归调用检查它的左右子树是否满足,如果都满足才说明这是一棵二叉搜索树。
方法一:递归
class Solution {public boolean isValidBST(TreeNode root) {//1.构建递归函数,传递左右区间参数return helper(root, Long.MIN_VALUE, Long.MAX_VALUE);}public boolean helper(TreeNode root, long lower, long upper){if(root == null){ //特殊情况判断return true;}//2.判断当前root结点if(root.val <= lower || root.val >= upper){return false;}//3.递归判断左右孩子结点return helper(root.left, lower, root.val) && helper(root.right, root.val, upper);}
}
Long.MIN_VALUE
和Long.MAX_VALUE
是啥?
Long.MIN_VALUE
是-2^63
,即-9,223,372,036,854,775,808
。代表long
类型变量可以存储的最小值。Long.MAX_VALUE
是2^63 - 1
,即9,223,372,036,854,775,807
。代表long
类型变量可以存储的最大值。
方法二:中序遍历
class Solution {public boolean isValidBST(TreeNode root) {if(root == null){return true;}//queue: 创建 -> 当前 -> 孩子//stack: 创建 -> 左移 -> 右移//1.创建Deque<TreeNode> stack = new LinkedList<>();long inorder = Long.MIN_VALUE;while(!stack.isEmpty() || root != null){//2.左移while(root != null){stack.push(root);root = root.left;}root = stack.pop();if(root.val <= inorder){return false;}inorder = root.val;//3.右移root = root.right;}return true;}
}
04 二叉搜索树中第K小的元素
方法一:递归
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {int flag1, flag2;public int kthSmallest(TreeNode root, int k) {flag1 = k;flag2 = 0;return inorder(root);}//1.创建public int inorder(TreeNode root){if(root == null){return -1;}//2.左右孩子结点int left = inorder(root.left);if(left != -1){return left;}flag2++;if(flag2 == flag1){return root.val;}//3.返回return inorder(root.right);}
}
方法二:栈
05 二叉树的右视图
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public List<Integer> rightSideView(TreeNode root) {}
}
方法一:深度优先搜索(stack)
class Solution {public List<Integer> rightSideView(TreeNode root) {Map<Integer, Integer> map = new HashMap<>(); //深度,valint maxDepth = -1; //标记//1.创建并加入Deque<TreeNode> nodeStack = new LinkedList<>();Deque<Integer> depthStack = new LinkedList<>();nodeStack.push(root);depthStack.push(0);while(!nodeStack.isEmpty()){//2.弹出并判断TreeNode node = nodeStack.pop();int depth = depthStack.pop();if(node != null){maxDepth = Math.max(maxDepth, depth); //标记更新//⭐if(!map.containsKey(depth)){map.put(depth, node.val);}//3.左右孩子结点nodeStack.push(node.left);nodeStack.push(node.right);depthStack.push(depth + 1);depthStack.push(depth + 1);}}//⭐List<Integer> result = new ArrayList<>();for(int i=0; i<=maxDepth; i++){result.add(map.get(i));}return result;}
}
① Deque
是啥意思?
Deque
是 Double Ended Queue
(双端队列) 的缩写,是一个接口,常用实现有LinkedList
和ArrayDeque
,既可以用作队列(先进先出),也可以用作栈(后进先出)。
② 这个代码是怎么保证每次栈弹出的都是没加入map的最右端结点?
因为栈是后进先出,右孩子会先被弹出访问。所以在访问该层节点时,第一个被访问到的节点是右孩子,也就是该层最右边的节点。
③ 为什么maxDepth
赋值为0,会出现解答错误?
- 当树为空时,没有层被访问,
maxDepth
应该还是未初始化状态。 - 如果用
int maxDepth = 0;
,说明认为至少访问了一层深度(深度0)。 - 代码循环从
0
到maxDepth
,会执行一次。 - 但实际上没有节点,也没往
map
中放任何数据,所以map.get(0)
会返回null
,结果列表加了一个null
。 - 导致与预期的空列表(
[]
)不符。
方法二:广度优先搜索(queue)
class Solution {public List<Integer> rightSideView(TreeNode root) {Map<Integer, Integer> rightmostValueAtDepth = new HashMap<Integer, Integer>();int max_depth = -1;Queue<TreeNode> nodeQueue = new LinkedList<TreeNode>();Queue<Integer> depthQueue = new LinkedList<Integer>();nodeQueue.add(root);depthQueue.add(0);while (!nodeQueue.isEmpty()) {TreeNode node = nodeQueue.remove();int depth = depthQueue.remove();if (node != null) {// 维护二叉树的最大深度max_depth = Math.max(max_depth, depth);// 由于每一层最后一个访问到的节点才是我们要的答案,因此不断更新对应深度的信息即可rightmostValueAtDepth.put(depth, node.val);nodeQueue.add(node.left);nodeQueue.add(node.right);depthQueue.add(depth + 1);depthQueue.add(depth + 1);}}List<Integer> rightView = new ArrayList<Integer>();for (int depth = 0; depth <= max_depth; depth++) {rightView.add(rightmostValueAtDepth.get(depth));}return rightView;}
}
为什么最后访问到的才是最右边节点?
rightmostValueAtDepth.put(depth, node.val)
; 不断地更新当前深度对应的节点值。- 访问顺序是每层从左到右,意味着:
- 最开始访问的节点会写入该层的值。
- 后面访问的节点会覆盖之前的值。
- 因为是从左往右访问,最后访问的节点就是最右节点。
- 最终 “rightmostValueAtDepth” 保存的,正是每一层的最右边节点值。