LeetCode_二叉树
二叉树
- 1、二叉树思路总结
- 1.1 树的自顶向下和自底向上遍历
- 1.2 深度和高度
- 1.3 递归函数有无返回值的判断
- 1.4 前中后序两两组合确定二叉树
- 1.5 递归函数有返回值时,如何区分搜索一条边还是搜索整个树
- 1.6 递归函数前加if
- 2、二叉树的前序遍历(力扣144)
- 3、二叉树的后序遍历(力扣145)
- 4、二叉树的中序遍历(力扣94)
- 5、 二叉树的层序遍历(力扣102)
- 6、二叉树的层序遍历 II(力扣107)
- 7、二叉树的右视图(力扣199)
- 8、二叉树的层平均值(力扣637)
- 9、N 叉树的层序遍历(力扣429)
- 10、在每个树行中找最大值(力扣515)
- 11、填充每个节点的下一个右侧节点指针(力扣116)
- 12、填充每个节点的下一个右侧节点指针 II(力扣117)
- 13、二叉树的最大深度(力扣104)
- 14、二叉树的最小深度(力扣111)
- 15、完全二叉树的节点个数(力扣222)
- 16、平衡二叉树(力扣110)
- 17、二叉树的所有路径(力扣257)
- 18、左叶子之和(力扣404)
- 19、找树左下角的值(力扣513)
- 20、路径总和(力扣112)
- 21、路径总和 II(力扣113)
- 22、从中序与后序遍历序列构造二叉树(力扣106)
- 23、从前序与中序遍历序列构造二叉树(力扣105)
- 24、最大二叉树(力扣654)
- 25、合并二叉树(力扣617)
- 26、验证二叉搜索树(力扣98)
- 27、二叉搜索树中的搜索(力扣700)
- 28、二叉搜索树的最小绝对差(力扣530)
- 29、二叉搜索树中的众数(力扣501)
- 30、二叉树的最近公共祖先(力扣236)
- 31、二叉搜索树的最近公共祖先(力扣235)
- 32、二叉搜索树中的插入操作(力扣701)
- 33、删除二叉搜索树中的节点(力扣450)
- 34、修剪二叉搜索树(力扣669)
- 35、将有序数组转换为二叉搜索树(力扣108)
- 36、把二叉搜索树转换为累加树(力扣538)
- 37、翻转二叉树(力扣226)
1、二叉树思路总结
1.1 树的自顶向下和自底向上遍历
- 自顶向下
前序遍历:先看当前节点的情况,再看当前节点的左子节点和右子节点的情况,然后层层向下,但是存在的问题是可能存在重复遍历的情况。 - 自底向上
后序遍历:先看当前节点的左子节点和右子节点的情况,再看当前节点的情况,然后层层向上,不存在重复遍历,节省复杂度。典型题:平衡二叉树(力扣110)
1.2 深度和高度
- 二叉树的高度和深度是等价的。
- 二叉树节点的深度:指从根节点到该节点的最长路径包含的节点个数。求深度:可以从上到下去查 所以需要前序遍历(中左右)。
二叉树节点的高度:指从该节点到叶子节点的最长路径包含的节点个数。求高度:从下到上去查,所以只能后序遍历(左右中)。
1.3 递归函数有无返回值的判断
- 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(113.路径总和ii)
- 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。 (236. 二叉树的最近公共祖先)
- 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(路径总和112)
1.4 前中后序两两组合确定二叉树
- 前序和中序可以唯一确定一棵二叉树。
- 后序和中序可以唯一确定一棵二叉树。
- 前序和后序不能唯一确定一棵二叉树
1.5 递归函数有返回值时,如何区分搜索一条边还是搜索整个树
- 搜索一条边的写法(直接返回)
if(递归函数(root.left)) return;
if(递归函数(root.right)) return;
- 搜索整个树的写法(用left和right变量接住返回值,进行逻辑处理)
left = 递归函数(root.left)
right = 递归函数(root.right)
left 和 right 的逻辑处理
1.6 递归函数前加if
什么时候递归函数前面加if,什么时候不加if?一般情况来说:如果让空节点(空指针)进入递归,就不加if,如果不让空节点进入递归,就加if限制一下, 终止条件也会相应的调整。
2、二叉树的前序遍历(力扣144)
//1.递归public List<Integer> preorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();preOrder(root,list);return list;}public void preOrder(TreeNode root,List<Integer> list){if(root == null){return;}list.add(root.val);preOrder(root.left,list);preOrder(root.right,list);}
//2.迭代,利用栈public static List<Integer> preorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();if(root == null){return list;}Deque<TreeNode> stack = new LinkedList<>();stack.push(root);while(!stack.isEmpty()){TreeNode node = stack.pop();list.add(node.val);if(node.right != null){stack.push(node.right);}if(node.left != null){stack.push(node.left);}}return list;}
3、二叉树的后序遍历(力扣145)
//1.递归public List<Integer> postorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();postOrder(root,list);return list;}public void postOrder(TreeNode root,List<Integer> list){if(root == null){return;}postOrder(root.left,list);postOrder(root.right,list);list.add(root.val);}
//2.迭代,利用栈public List<Integer> postorderTraversal2(TreeNode root) {List<Integer> list = new ArrayList<>();if(root == null){return list;}Deque<TreeNode> stack = new LinkedList<>();stack.push(root);while(!stack.isEmpty()){TreeNode node = stack.pop();list.add(node.val);if(node.left != null){stack.push(node.left);}if(node.right != null){stack.push(node.right);}}Collections.reverse(list);return list;}
建议后序遍历时使用下面这种方式,第二种方法只是最终结果是正确的,其实际遍历顺序根本不是后序遍历
//3.迭代法,完全按照顺序,不是反转public List<Integer> postorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();if(root == null) return list;Deque<TreeNode> stack = new LinkedList<>();//由于在某颗子树访问完成以后,接着就要回溯到其父节点去//因此可以用prev来记录访问历史,在回溯到父节点时,可以由此来判断,上一个访问的节点是否为右子树TreeNode cur = root,pre = null;while(cur != null || !stack.isEmpty()){while(cur != null){stack.push(cur);cur = cur.left;}//从栈中弹出的节点,其左子树一定已经访问了cur = stack.pop();//如果没有右子树或者右子树已经访问完了,表示可以访问当前节点了if(cur.right == null || cur.right == pre){list.add(cur.val);//更新pre,记录上一个访问的节点pre = cur;//切记将cur置为空cur = null;}else{//如果右子树还没有访问,先访问右子树stack.push(cur);cur = cur.right;}}return list;}
4、二叉树的中序遍历(力扣94)
//1.递归public List<Integer> inorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();inOrder(root,list);return list;}public void inOrder(TreeNode root,List<Integer> list){if(root == null){return;}inOrder(root.left,list);list.add(root.val);inOrder(root.right,list);}
//2.迭代。利用栈public List<Integer> inorderTraversal2(TreeNode root) {List<Integer> list = new ArrayList<>();if(root == null){return list;}Deque<TreeNode> stack = new LinkedList<>();TreeNode cur = root;while(cur != null || !stack.isEmpty()){//沿着左子树一直找到直到为空为止if(cur != null){stack.push(cur);cur = cur.left;}else{//开始往外弹栈cur = stack.pop();list.add(cur.val);//弹栈过程中考虑右子树cur = cur.right;}}return list;}
5、 二叉树的层序遍历(力扣102)
//利用队列public static List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> list = new ArrayList<>();if(root == null){return list;}Queue<TreeNode> queue = new LinkedList<>();queue.add(root);while(!queue.isEmpty()){List<Integer> level = new ArrayList<>();int len = queue.size();for(int i = 0;i < len;i ++){TreeNode node = queue.poll();level.add(node.val);if(node.left != null){queue.add(node.left);}if(node.right != null){queue.add(node.right);}}list.add(level);}return list;}
6、二叉树的层序遍历 II(力扣107)
//1.利用栈来反转结果public static List<List<Integer>> levelOrderBottom(TreeNode root) {List<List<Integer>> list = new ArrayList<>();if(root == null){return list;}Queue<TreeNode> queue = new LinkedList<>();Deque<List<Integer>> stack = new LinkedList<>();queue.add(root);while(!queue.isEmpty()){List<Integer> level = new ArrayList<>();int len = queue.size();for(int i = 0;i < len;i ++){TreeNode node = queue.poll();level.add(node.val);if(node.left != null){queue.add(node.left);}if(node.right != null){queue.add(node.right);}}stack.push(level);}while(!stack.isEmpty()){list.add(stack.pop());}return list;}
//2.利用方法 add(index,List<Integer> element)public static List<List<Integer>> levelOrderBottom1(TreeNode root) {List<List<Integer>> list = new ArrayList<>();if(root == null){return list;}Queue<TreeNode> queue = new LinkedList<>();queue.add(root);while(!queue.isEmpty()){List<Integer> level = new ArrayList<>();int len = queue.size();for(int i = 0;i < len;i ++){TreeNode node = queue.poll();level.add(node.val);if(node.left != null){queue.add(node.left);}if(node.right != null){queue.add(node.right);}}//每次后加入的放在最前面list.add(0,level);}return list;}
7、二叉树的右视图(力扣199)
//1.利用队列先进先出public static List<Integer> rightSideView(TreeNode root) {List<Integer> list = new ArrayList<>();if(root == null){return list;}Queue<TreeNode> queue = new LinkedList<>();queue.add(root);while(!queue.isEmpty()){int len = queue.size();for(int i = 0;i < len;i ++){TreeNode node = queue.poll();//只将每一层的最后一个加入到结果集if(i == len - 1){list.add(node.val);}if(node.left != null){queue.add(node.left);}if(node.right != null){queue.add(node.right);}}}return list;}
8、二叉树的层平均值(力扣637)
//1.利用队列public static List<Double> averageOfLevels(TreeNode root) {List<Double> list = new ArrayList<>();Queue<TreeNode> queue = new LinkedList<>();queue.add(root);while(!queue.isEmpty()){int len = queue.size();double sum = 0;for(int i = 0;i < len;i++){TreeNode node = queue.poll();sum += node.val;if(node.left != null){queue.add(node.left);}if(node.right != null){queue.add(node.right);}}list.add(sum / len);}return list;}
9、N 叉树的层序遍历(力扣429)
//1.利用队列public List<List<Integer>> levelOrder(Node root) {List<List<Integer>> list = new ArrayList<>();if(root == null){return list;}Queue<Node> queue = new LinkedList<>();queue.add(root);while(!queue.isEmpty()){List<Integer> level = new ArrayList<>();int len = queue.size();for(int i = 0;i < len;i ++){Node node = queue.poll();level.add(node.val);if(node.children != null){List<Node> cur = node.children;for(Node n : cur){queue.add(n);}}}list.add(level);}return list;}
10、在每个树行中找最大值(力扣515)
//1.利用队列public List<Integer> largestValues(TreeNode root) {List<Integer> list = new ArrayList<>();if(root == null){return list;}Queue<TreeNode> queue = new LinkedList<>();queue.add(root);while(!queue.isEmpty()){int len = queue.size();int max = Integer.MIN_VALUE;for(int i = 0;i < len;i ++){TreeNode node = queue.poll();max = Math.max(max,node.val);if(node.left != null){queue.add(node.left);}if(node.right != null){queue.add(node.right);}}list.add(max);}return list;}
11、填充每个节点的下一个右侧节点指针(力扣116)
//1.利用队列,但是不是常数空间public static Node connect(Node root) {if(root == null){return root;}Queue<Node> queue = new LinkedList<>();queue.add(root);while(!queue.isEmpty()){int len = queue.size();for(int i = 0;i < len;i ++){Node node1 = queue.poll();if(i < len - 1) {node1.next = queue.peek();}if(node1.left != null){queue.add(node1.left);}if(node1.right != null){queue.add(node1.right);}}}return root;}
//2.常数空间
class Solution {public Node connect(Node root) {if (null == root){return root;}/* 站在当前树层,将下一层的结点串接起来。此结点用于标识每一层最左边的结点 */Node mostLeftNode = root;/* 如果有下一层 */while (mostLeftNode.left != null){Node curNode = mostLeftNode;/* 从左到右移动 */while (curNode != null){curNode.left.next = curNode.right;if (curNode.next != null){curNode.right.next = curNode.next.left;}curNode = curNode.next;}/* 一层遍历完,从下一层的最左边开始 */mostLeftNode = mostLeftNode.left;}return root;}
}
12、填充每个节点的下一个右侧节点指针 II(力扣117)
//1.利用队列,但不是常量空间public Node connect1(Node root) {if(root == null){return root;}Queue<Node> queue = new LinkedList<>();queue.add(root);while(!queue.isEmpty()){int len = queue.size();for(int i = 0;i < len;i ++){Node node = queue.poll();if(i < len - 1){node.next = queue.peek();}if(node.left != null){queue.add(node.left);}if(node.right != null){queue.add(node.right);}}}return root;}
// 2.常数空间
class Solution {public Node connect(Node root) {if (root == null){return root;}Node curNode = root;/* 遍历本层,连接下层 */while (curNode != null){/* 维持每一层最左边的结点 */Node mostLeftNode = new Node(0);/* 遍历到的结点的前一个结点 */Node preNode = mostLeftNode;while (curNode != null){if (curNode.left != null){preNode.next = curNode.left;preNode = preNode.next;}if (curNode.right != null){preNode.next = curNode.right;preNode = preNode.next;}/* 继续向右遍历 */curNode = curNode.next;}/* 切换到下一层 */curNode = mostLeftNode.next;}return root;}
}
13、二叉树的最大深度(力扣104)
//1.递归public static int maxDepth(TreeNode root) {if(root == null){return 0;}return Math.max(maxDepth(root.left),maxDepth(root.right)) + 1;}
//2.利用队列,遍历完一层深度加1public int maxDepth2(TreeNode root) {if(root == null){return 0;}Queue<TreeNode> queue = new LinkedList<>();queue.add(root);int deepth = 0;while(!queue.isEmpty()){int len = queue.size();for(int i = 0;i < len;i ++){TreeNode node = queue.poll();if(node.left != null){queue.add(node.left);}if(node.right != null){queue.add(node.right);}}deepth ++;}return deepth;}
14、二叉树的最小深度(力扣111)
此题注意点:找到离根节点最近的那个“叶子节点”
//1.递归/** 1.确定递归函数参数和返回值。传入一个节点,返回以此节点为根节点的树的最小深度* 2.确定递归的终止条件。当所传入的节点为null,返回为0* 3.确定每一层的逻辑。* 1.当前节点为null,返回0;2.当前节点的左子节点和右子节点均为null,返回1;* 3.当前节点的左节点或右节点为null,返回 len1 + len2 + 1* 4.当前节点的左子节点和右子节点均不为null,返回以两者为根节点的子树的较小值* */public static int minDepth(TreeNode root) {if(root == null){return 0;}if(root.left == null && root.right == null){return 1;}int len1 = minDepth(root.left);int len2 = minDepth(root.right);if(root.left == null || root.right == null){return len1 + len2 + 1;}return Math.min(len1,len2) + 1;}
//对递归进行简化,情况2和情况4可以合二为一;情况3可以拆分为两个条件public static int minDepth(TreeNode root) {if(root == null){return 0;}if(root.left == null && root.right != null){return minDepth(root.right) + 1;}if(root.right == null && root.left != null){return minDepth(root.left) + 1;}return Math.min(minDepth(root.left),minDepth(root.right)) + 1;}
//2.利用队列,层次遍历public int minDepth2(TreeNode root) {if(root == null){return 0;}int depth = 1;Queue<TreeNode> queue = new LinkedList<>();queue.add(root);while(!queue.isEmpty()){int len = queue.size();for(int i = 0;i < len;i++){TreeNode node = queue.poll();//找到了一个叶子节点,直接返回结果if(node.left == null && node.right == null){return depth;}if(node.left != null){queue.add(node.left);}if(node.right != null){queue.add(node.right);}}depth ++;}return depth;}
15、完全二叉树的节点个数(力扣222)
//1.递归//首先确定递归函数的参数和返回值//参数:传入一个节点;返回值:返回以此节点为根节点的树的节点总数量public int countNodes(TreeNode root) {//如果当前节点为null,直接返回0if(root == null){return 0;}//以此节点的左子节点为根节点的树的节点数量int leftCount = countNodes(root.left);//以此节点的右子节点为根节点的树的节点数量int rightCount = countNodes(root.right);//返回以此节点为根节点的树的节点数量为 leftCount + rightCount + 此节点的数量1return leftCount + rightCount + 1;}
//2.考虑完全二叉树的特点public int countNodes1(TreeNode root) {if(root == null){return 0;}//计算以根节点的左子节点为根节点的完全二叉树的高度int leftDepth = maxDepth(root.left);//计算以根节点的右子节点为根节点的完全二叉树的高度int rightDepth = maxDepth(root.right);//如果相等,说明以根节点的左子节点为根节点的完全二叉树是满二叉树,总的节点数量为//此满二叉树节点数量+根节点数量1+以根节点的右子节点为根节点的完全二叉树的节点数量if(leftDepth == rightDepth){return (int)Math.pow(2,leftDepth) + count(root.right);//如果不相等,说明以根节点的右子节点为根节点的完全二叉树是满二叉树,总的节点数量为//此满二叉树节点数量+根节点数量1+以根节点的左子节点为根节点的完全二叉树的节点数量}else{return (int)Math.pow(2,rightDepth) + count(root.left);}}//计算以此节点为根节点的完全二叉树的节点个数public int count(TreeNode root){if(root == null){return 0;}return count(root.left) + count(root.right) + 1;}//计算以此节点为根节点的完全二叉树的高度public int maxDepth(TreeNode root){if(root == null){return 0;}return maxDepth(root.left) + 1;}
//3.递归//返回以此节点为根节点的树的节点的数量public int countNodes3(TreeNode root) {if(root == null){return 0;}int leftDepth = Depth(root.left);int rightDepth = Depth(root.right);if(leftDepth == rightDepth){return (int)Math.pow(2,leftDepth) + countNodes(root.right);}else{return (int)Math.pow(2,rightDepth) + countNodes(root.left);}}//迭代,返回以此节点为根节点的树的节点的高度public int Depth(TreeNode root){if(root == null){return 0;}int depth = 1;while(root.left != null){root = root.left;depth ++;}return depth;}
16、平衡二叉树(力扣110)
//1.二叉树的层次遍历,判断每一个节点的左右子树的高度是否满足要求(自顶向下)public boolean isBalanced(TreeNode root) {if(root == null){return true;}Queue<TreeNode> queue = new LinkedList<>();queue.add(root);while(!queue.isEmpty()){int len = queue.size();for(int i = 0;i < len;i ++){TreeNode node = queue.poll();if(Math.abs(Depth(node.left) - Depth(node.right)) > 1){return false;}if(node.left != null){queue.add(node.left);}if(node.right != null){queue.add(node.right);}}}return true;}//传入一个节点,返回以此节点为根节点的树高度public int Depth(TreeNode root){if(root == null){return 0;}return Math.max(Depth(root.left),Depth(root.right)) + 1;}
//2.递归(自顶向下)public boolean isBalanced(TreeNode root) {if(root == null){return true;}if(Math.abs(Depth(root.left) - Depth(root.right)) > 1){return false;}return isBalanced(root.left) && isBalanced(root.right);}//输入一个节点,返回以此节点为根节点的树的高度public int Depth(TreeNode root){if(root == null){return 0;}return Math.max(Depth(root.left),Depth(root.right)) + 1;}
//3.递归(自底向上)//后续遍历,先看最底下的树是否是平衡二叉树,再层层往上递归//时间和空间复杂度都是o(n)public boolean isBalanced(TreeNode root) {return Depth(root) >= 0;}public int Depth(TreeNode root){if(root == null){return 0;}int leftDepth = Depth(root.left);int rightDepth = Depth(root.right);if((leftDepth == -1) || (rightDepth == -1) || Math.abs(leftDepth - rightDepth) > 1){return -1;}else{return Math.max(leftDepth,rightDepth) + 1;}}
17、二叉树的所有路径(力扣257)
//1.递归//时间和空间复杂度都是O(n2)public List<String> binaryTreePaths(TreeNode root) {List<String> res = new ArrayList<>();if(root == null){return res;}List<Integer> path = new ArrayList<>();travelsal(root,path,res);return res;}//递归+回溯//递归函数:每次传入当前节点;path记录从根节点到此节点的路径上的所有节点,便于回溯;//res记录最终的结果,每到达一个左右节点均为null的节点,就回溯从根节点到此节点路径//上的所有节点,连成一个String,放到res中public void travelsal(TreeNode root, List<Integer> path, List<String> res){path.add(root.val);if(root.left == null && root.right == null){int len = path.size();StringBuilder sb = new StringBuilder();for(int i = 0;i < len - 1;i ++){sb.append(path.get(i));sb.append("->");}sb.append(path.get(len - 1));res.add(sb.toString());}if(root.left != null){travelsal(root.left,path,res);//到这一步,说明上面加入的root.left节点已经用过了,需要及时删除path.remove(path.size() - 1);}if(root.right != null){travelsal(root.right,path,res);//到这一步,说明上面加入的root.right节点已经用过了,需要及时删除path.remove(path.size() - 1);}}
//2.迭代public List<String> binaryTreePaths2(TreeNode root) {List<String> res = new ArrayList<>();if(root == null){return res;}Deque<Object> stack = new LinkedList<>();//装入节点stack.push(root);//装入从根节点到此节点的路径stack.push(root.val + "");while(!stack.isEmpty()){//取出到此节点的路径String path = (String)stack.pop();//取出此节点TreeNode node = (TreeNode)stack.pop();//如果当前节点的左右子节点均为null,说明此节点为叶子结点,将路径放入返回集合if(node.left == null && node.right == null){res.add(path);}//如果左子节点不为null,将左子节点和到左子节点的路径压栈if(node.left != null){stack.push(node.left);stack.push(path + "->" + node.left.val);}//同样处理右子节点if(node.right != null){stack.push(node.right);stack.push(path + "->" + node.right.val);}}return res;}
//3.递归(时空复杂度都是O(n2))public List<String> binaryTreePaths3(TreeNode root) {List<String> res = new ArrayList<>();if(root == null) return res;binary(res,new StringBuilder(),root);return res;}public void binary(List<String> res,StringBuilder sb,TreeNode root){sb.append(root.val);if(root.left == null && root.right == null){res.add(sb.toString());}if(root.left != null){//因为每次传进去的都是new的StringBuilder,不用回溯binary(res,new StringBuilder(sb).append("->"),root.left);}if(root.right != null){binary(res,new StringBuilder(sb).append("->"),root.right);}}
18、左叶子之和(力扣404)
//1.层序遍历(当然也可以使用其他的遍历方式,判断每一个节点的左子节点是不是叶子节点)public int sumOfLeftLeaves(TreeNode root) {int sum = 0;if(root == null){return sum;}Queue<TreeNode> queue = new LinkedList<>();queue.add(root);while(!queue.isEmpty()){int len = queue.size();for(int i = 0;i < len;i ++){TreeNode node = queue.poll();//如果当前节点的左子节点不为空,并且当前节点的左子节点的左子节点和右子节点均为空,则此节点的左子节点为左叶子节点if(node.left != null && node.left.left == null && node.left.right == null){sum += node.left.val;}if(node.left != null){queue.add(node.left);}if(node.right != null){queue.add(node.right);}}}return sum;}
//2.递归// 递归函数:每次传入一个节点,返回以此节点为根节点的树的左叶子节点的和//自底向上,使用后序遍历public int sumOfLeftLeaves2(TreeNode root) {if(root == null){return 0;}int leftValue = sumOfLeftLeaves(root.left);//左int rightValue = sumOfLeftLeaves(root.right);//右int midValue = 0;//中if(root.left != null && root.left.left == null && root.left.right == null){midValue = root.left.val;}return leftValue + rightValue + midValue;}
19、找树左下角的值(力扣513)
//1.层序遍历,每遍历到每层的第一个节点时,将返回值替换为此节点值public int findBottomLeftValue(TreeNode root) {Queue<TreeNode> queue = new LinkedList<>();queue.add(root);int res = root.val;boolean flag = true;while(!queue.isEmpty()){int len = queue.size();for(int i = 0;i < len;i ++){TreeNode node = queue.poll();if(flag){res = node.val;flag = false;}if(node.left != null){queue.add(node.left);}if(node.right != null){queue.add(node.right);}}flag = true;}return res;}
//2.递归,采用前序遍历//用来记录最大深度private int Deep = -1;//返回值private int value = 0;public int findBottomLeftValue2(TreeNode root) {value = root.val;findLeftValue(root,0);return value;}//递归函数//传入节点和当前节点的深度//采用前序遍历,保证每次到达最大深度所取得值是最左边的private void findLeftValue(TreeNode root,int deep){if(root == null){return;}if(root.left == null && root.right == null){//保证每次到达一个更大深度时,所取的值是最左边的if(deep > Deep){value = root.val;Deep = deep;}}findLeftValue(root.left,deep + 1);findLeftValue(root.right,deep + 1);}
20、路径总和(力扣112)
//1.递归,每次传入递归函数的是相减的值,代码比较简洁public boolean hasPathSum2(TreeNode root, int targetSum) {if(root == null){return false;}if(root.left == null && root.right == null && targetSum - root.val == 0){return true;}return hasPathSum(root.left,targetSum - root.val) || hasPathSum(root.right,targetSum - root.val);}
//2.层序遍历,利用两个队列,分别存放节点和到此节点路径上的节点的和public boolean hasPathSum3(TreeNode root, int targetSum) {if(root == null){return false;}Queue<TreeNode> queueNode = new LinkedList<>();Queue<Integer> queueInt = new LinkedList<>();queueNode.add(root);queueInt.add(root.val);while(!queueNode.isEmpty()){int len = queueNode.size();for(int i = 0;i < len;i ++){TreeNode node = queueNode.poll();int value = queueInt.poll();if(node.left == null && node.right == null && value == targetSum){return true;}if(node.left != null){queueNode.add(node.left);queueInt.add(node.left.val + value);}if(node.right != null){queueNode.add(node.right);queueInt.add(node.right.val + value);}}}return false;}
21、路径总和 II(力扣113)
其实就是回溯
//1.递归public List<List<Integer>> pathSum(TreeNode root, int targetSum) {List<List<Integer>> list = new ArrayList<>();if(root == null){return list;}List<Integer> curList = new ArrayList<>();path(root,targetSum,list,curList);return list;}//递归函数,每次传入一个节点,为达到targetSum还需要多大的数值,最终返回结果,从根节点到此节点前一个节点的所有节点的集合public void path(TreeNode root,int targetSum,List<List<Integer>> list,List<Integer> curList){curList.add(root.val);if(root.left == null && root.right == null && targetSum == root.val){//注意,每次满足要求时向list添加时要new一个arraylist//最开始自己写的时候:list.add(curList);list.add(new ArrayList<Integer>(curList));}if(root.left != null){path(root.left,targetSum - root.val,list,curList);curList.remove(curList.size() - 1);}if(root.right != null){path(root.right,targetSum - root.val,list,curList);curList.remove(curList.size() - 1);}}
22、从中序与后序遍历序列构造二叉树(力扣106)
//1.递归public TreeNode buildTree(int[] inorder, int[] postorder) {return build(inorder,0,inorder.length,postorder,0,postorder.length);}//递归函数,每次传入://树的中序遍历数组及范围,范围是左闭右开;树的后序遍历数组及范围,范围是左闭右开public TreeNode build(int[] inorder,int inLeft,int inRight,int[] postorder,int postLeft,int postRight) {//没有元素了if (inRight - inLeft < 1) {return null;}//只有一个元素了,直接返回if (inRight - inLeft == 1) {return new TreeNode(inorder[inLeft]);}//否则的话,根据根节点进行分割//后序遍历数组的最后一个元素就是根节点int rootValue = postorder[postRight - 1];TreeNode root = new TreeNode(rootValue);//记录根节点在中序遍历数组中的下标,并以此为分割点,将数组分割为左右两部分int rootIndex = 0;for (int i = 0; i < inRight; i++) {if (inorder[i] == rootValue) {rootIndex = i;}}//中序数组根据分割点分为两部分,后序数组也分为两部分,中序和后序分别左对左,右对右,继续递归//注意每次的范围是 左闭右开root.left = build(inorder, inLeft, rootIndex, postorder, postLeft, postLeft + rootIndex - inLeft);root.right = build(inorder, rootIndex + 1, inRight, postorder, postLeft + rootIndex - inLeft, postRight - 1);//返回根节点return root;}
23、从前序与中序遍历序列构造二叉树(力扣105)
//1.递归public TreeNode buildTree(int[] preorder, int[] inorder) {return build(preorder,0,preorder.length,inorder,0,inorder.length);}public TreeNode build(int[] preorder,int preLeft,int preRight,int[] inorder,int inLeft,int inRight){if(inRight - inLeft < 1){return null;}if(inRight - inLeft == 1){return new TreeNode(inorder[inLeft]);}int rootvalue = preorder[preLeft];TreeNode root = new TreeNode(rootvalue);int rootIndex = 0;for(int i = 0;i < inRight;i ++){if(inorder[i] == rootvalue){rootIndex = i;}}root.left = build(preorder,preLeft + 1,preLeft + rootIndex - inLeft + 1,inorder,inLeft,rootIndex);root.right = build(preorder,preLeft + rootIndex - inLeft + 1,preRight,inorder,rootIndex + 1,inRight);return root;}
24、最大二叉树(力扣654)
public TreeNode constructMaximumBinaryTree(int[] nums) {return construst(nums,0,nums.length);}//递归函数:每次传入原始数组以及所需数组的起止下标,范围是左闭右开//一般来说,不用每次传入所需数组,传入所需数组在原始数组中对应的下标就可以,比较简洁public TreeNode construst(int[] nums,int left,int right){//如果数组范围为空,直接returnif(right - left < 1){return null;}//如果数组只有一个元素,直接作为节点返回if(right - left == 1){return new TreeNode(nums[left]);}//定义所需数组范围内的最大值和最大值对应的下标int maxValue = 0;int maxIndex = 0;for(int i = left;i < right;i ++){if(nums[i] > maxValue){maxValue = nums[i];maxIndex = i;}}TreeNode node = new TreeNode(maxValue);//递归调用次函数,构造左右子树node.left = construst(nums,left,maxIndex);node.right = construst(nums,maxIndex + 1,right);return node;}
25、合并二叉树(力扣617)
//1.递归//递归函数:每次传入对应位置上的两个节点,返回两个节点组合之后的节点public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {//当其中一个节点为null,则以此节点为根节点的树无需考虑,返回以另一个节点为根节点的树即可if(root1 == null){return root2;}if(root2 == null){return root1;}//当两个节点均不为null,则需要构造一个新节点,并调用递归函数TreeNode node = new TreeNode(root1.val + root2.val);node.left = mergeTrees(root1.left,root2.left);node.right = mergeTrees(root1.right,root2.right);return node;}
//2.迭代(利用队列)public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {if(root1 == null) return root2;if(root2 == null) return root1;Deque<TreeNode> deque = new LinkedList<>();deque.add(root1);deque.add(root2);while(!deque.isEmpty()){TreeNode node1 = deque.poll();TreeNode node2 = deque.poll();//直接以root1为根节点的树为基础进行修改//如果对应的两个节点都不为null,则将node1的节点值修改为两节点值之和node1.val = node1.val + node2.val;if(node1.left != null && node2.left != null){deque.add(node1.left);deque.add(node2.left);}if(node1.right != null && node2.right != null){deque.add(node1.right);deque.add(node2.right);}//只有两个节点的左或右节点均不为null,才将节点放入队列//否则直接将node2为根节点的树拼接到node1后,没必要再放入队列中if(node1.left == null){node1.left = node2.left;}if(node1.right == null){node1.right = node2.right;}}//因为是在以root1为根节点的树上做修改,所以最终返回root1即可return root1;}
26、验证二叉搜索树(力扣98)
//1.利用递归中序遍历得到结果,再判断所得结果是否是升序public boolean isValidBST(TreeNode root) {if(root.left == null && root.right == null) return true;List<Integer> list = new ArrayList<>();preOrder(root,list);for(int i = 0;i < list.size() - 1;i ++){if(list.get(i) >= list.get(i + 1)){return false;}}return true;}public void preOrder(TreeNode root,List<Integer> list){if(root == null) return;preOrder(root.left,list);list.add(root.val);preOrder(root.right,list);}
//2.递归:每次传入一个节点以及以此节点为根节点的二叉搜索树的所有节点的取值范围(左开右开)public boolean isValidBST2(TreeNode root) {return isValidBST(root,Long.MIN_VALUE,Long.MAX_VALUE);}public boolean isValidBST(TreeNode root,long lower,long upper){if(root == null) return true;//如果当前节点值不在要求范围内,返回falseif(root.val <= lower || root.val >= upper){return false;}return isValidBST(root.left,lower,root.val) && isValidBST(root.right,root.val,upper);}
//3.利用迭代判断树的中序遍历结果是否是升序//注意树的中序遍历迭代法和前序,后序不一样public boolean isValidBST3(TreeNode root) {if(root == null) return true;Long pre = Long.MIN_VALUE;Deque<TreeNode> stack = new LinkedList<>();//创建一个指针,用来指向按照“中序遍历”遍历的节点TreeNode cur = root;while(cur != null || !stack.isEmpty()){//一直向左遍历,直到找到树的最左边的节点if(cur != null){stack.push(cur);cur = cur.left;}else{//取出节点,作为“中”节点cur = stack.pop();if(cur.val <= pre){return false;}pre = (long)cur.val;//继续向右边遍历cur = cur.right;}}return true;}
27、二叉搜索树中的搜索(力扣700)
//1.递归public TreeNode searchBST(TreeNode root, int val) {if(root == null || root.val == val) return root;if(root.val > val){return searchBST(root.left,val);}else{return searchBST(root.right,val);}}
//2.迭代public TreeNode searchBST2(TreeNode root, int val) {while(root != null){if(root.val == val){return root;}else if(root.val > val){root = root.left;}else if(root.val < val){root = root.right;}}return null;}
28、二叉搜索树的最小绝对差(力扣530)
//1.按照中序遍历迭代,比较相邻数值的差值TreeNode preNode;public int getMinimumDifference(TreeNode root) {int minAbsNum = Integer.MAX_VALUE;Deque<TreeNode> stack = new LinkedList<>();TreeNode cur = root;while(cur != null || !stack.isEmpty()){if(cur != null){stack.push(cur);cur = cur.left;}else{cur = stack.pop();if(preNode != null){minAbsNum = Math.min(minAbsNum,cur.val - preNode.val);}preNode = cur;cur = cur.right;}}return minAbsNum;}
//2.先递归中序遍历得到结果,再依次比较相邻最小绝对值public int getMinimumDifference2(TreeNode root) {int minAbsNum;List<Integer> list = new ArrayList<>();preOrder(root,list);minAbsNum = list.get(1) - list.get(0);for(int i = 1;i < list.size() - 1;i ++){if(list.get(i + 1) - list.get(i) < minAbsNum){minAbsNum = list.get(i + 1) - list.get(i);}}return minAbsNum;}public void preOrder(TreeNode root,List<Integer> list){if(root == null) return;preOrder(root.left,list);list.add(root.val);preOrder(root.right,list);}
//3.递归//递归中序遍历二叉搜索树,preNode保存上一个节点,按照中序遍历顺序两两依次比较TreeNode preNode;int minAbsNum = Integer.MAX_VALUE;public int getMinimumDifference(TreeNode root) {inOrder(root);return minAbsNum;}public void inOrder(TreeNode root){if(root == null) return;inOrder(root.left);if(preNode != null){minAbsNum = Math.min(minAbsNum,root.val - preNode.val);}//在递归过程中保留上一个节点preNode = root;inOrder(root.right);}
29、二叉搜索树中的众数(力扣501)
//1.递归//等同于在有序数组中找众数//二叉树中序遍历递归和迭代写法int maxCount;int count;TreeNode preNode;List<Integer> list;public int[] findMode(TreeNode root) {maxCount = 0;count = 0;list = new ArrayList<>();preOrder(root,list);int[] ans = new int[list.size()];for(int i = 0;i < list.size();i ++){ans[i] = list.get(i);}return ans;}public void preOrder(TreeNode root,List<Integer> list){if(root == null) return;//向左遍历preOrder(root.left,list);//如果当前节点是根节点或是当前节点的值不等于前一个节点的值,计数器置为1if(preNode == null || preNode.val != root.val){count = 1;}else{count ++;}//如果计数器值大于当前已有最大频率值,清空列表if(count > maxCount){list.clear();list.add(root.val);maxCount = count;}else if(count == maxCount){list.add(root.val);}preNode = root;//向右遍历preOrder(root.right,list);}
//2.迭代int maxCount;int count;TreeNode preNode;List<Integer> list;public int[] findMode2(TreeNode root) {maxCount = 0;count = 0;list = new ArrayList<>();inOrder(root,list);int[] ans = new int[list.size()];for(int i = 0;i < list.size();i ++){ans[i] = list.get(i);}return ans;}public void inOrder(TreeNode root,List<Integer> list){Deque<TreeNode> stack = new LinkedList<>();TreeNode curNode = root;while(curNode != null || !stack.isEmpty()){if(curNode != null){stack.push(curNode);curNode = curNode.left;}else{curNode = stack.pop();if(preNode == null || preNode.val != curNode.val){count = 1;}else{count ++;}if(count > maxCount){list.clear();list.add(curNode.val);maxCount = count;}else if(count == maxCount){list.add(curNode.val);}preNode = curNode;curNode = curNode.right;}}}
30、二叉树的最近公共祖先(力扣236)
//1.递归//这就是一个后序遍历的模型,只不过是每个父节点都会接收子节点的状态//(是否含有p、q),并把这个状态往上传递,直到该结点满足祖先节点的条// 件。这样一想就豁然开朗了。//传入根节点,目标节点p和q;返回p和q的最近祖先节点public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if(root == null || root == p || root == q){return root;}TreeNode left = lowestCommonAncestor(root.left,p,q);TreeNode right = lowestCommonAncestor(root.right,p,q);//如果左右传上来的都不为空,则最近公共节点为当前节点if(left != null && right != null){return root;//如果左右传上来的一边为空,则返回不为空的一边,目的是为了告诉上层在这个方向找到了目标值//可能找到了一个,也可能两个都找到了}else if(left == null && right != null){return right;}else if(left != null && right == null){return left;//如果同时为null,说明左右两边都没有找到目标值}else{return null;}}
31、二叉搜索树的最近公共祖先(力扣235)
//1.迭代//从上到下遍历,如果发现当前节点值处于p和q值之间,则当前节点即为最近公共祖先public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if(root == null) return null;Deque<TreeNode> stack = new LinkedList<>();stack.push(root);while(!stack.isEmpty()){TreeNode node = stack.pop();if((node.val >= p.val && node.val <= q.val) || (node.val <= p.val && node.val >= q.val)){return node;}if(node.right != null){stack.push(node.right);}if(node.left != null){stack.push(node.left);}}return null;}
//2.迭代改进public TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q) {//从上到下遍历while(true){//向左遍历if(root.val > p.val && root.val > q.val){root = root.left;//向右遍历}else if(root.val < p.val && root.val < q.val){root = root.right;//如果发现当前节点值在p和q值范围内,直接跳出}else{break;}}//返回当前节点return root;}
//3.递归//注意递归时 递归一条路径 和 递归所有节点(力扣236递归法)的写法的不同public TreeNode lowestCommonAncestor3(TreeNode root, TreeNode p, TreeNode q) {if(root == null) return null;if(root.val > p.val && root.val > q.val){return lowestCommonAncestor(root.left,p,q);}else if(root.val < p.val && root.val < q.val){return lowestCommonAncestor(root.right,p,q);}return root;}
32、二叉搜索树中的插入操作(力扣701)
//1.递归//利用搜索二叉树的特性,找到插入位置public TreeNode insertIntoBST(TreeNode root, int val) {if(root == null) return new TreeNode(val);if(root.val > val){root.left = insertIntoBST(root.left,val);}else if(root.val < val){root.right = insertIntoBST(root.right,val);}return root;}
//2.迭代法public TreeNode insertIntoBST2(TreeNode root, int val) {if(root == null) return new TreeNode(val);//设置哨兵节点,保存根节点TreeNode headNode = root;while(root != null){if(val > root.val){if(root.right == null){root.right = new TreeNode(val);break;}root = root.right;}else if(val < root.val){if(root.left == null){root.left = new TreeNode(val);break;}root = root.left;}}return headNode;}
33、删除二叉搜索树中的节点(力扣450)
//1.递归//返回节点,供前一步连接/** 整个过程共有5种情况* 1.删除节点没找到,直接返回null* 2.删除节点找到了* 2.1 删除节点为叶子节点,直接删除* 2.2 删除结点左孩子为空,右孩子不为空,直接删除,右孩子返回* 2.3 删除节点右孩子为空,左孩子不为空,直接删除,左孩子返回* 2.4 删除节点左右孩子均不为空,将删除结点的左子树链接到删除* 结点的右孩子的最左边,返回删除节点的右孩子* */public TreeNode deleteNode(TreeNode root, int key) {if(root == null) return root;if(key < root.val){root.left = deleteNode(root.left,key);}else if(key > root.val){root.right = deleteNode(root.right,key);}else{if(root.left == null){return root.right;}else if(root.right == null){return root.left;}else{TreeNode tmpNode = root.right;while(tmpNode.left != null){tmpNode = tmpNode.left;}tmpNode.left = root.left;return root.right;}}return root;}
34、修剪二叉搜索树(力扣669)
//1.递归//和力扣450的不同://450:找到目标节点(只需要找一个)即结束遍历 669:遍历整棵树,找到所有不满足要求的节点进行删除public TreeNode trimBST(TreeNode root, int low, int high) {if(root == null){return null;}//遍历整棵树,意味着处理单层逻辑时不能出现“未包含递归函数”语句//当前节点值不满足要求,删除当前节点,向反方向遍历if(root.val < low){return trimBST(root.right,low,high);}else if(root.val > high){return trimBST(root.left,low,high);}else{//当前节点满足,保留当前节点,重构左右子树root.left = trimBST(root.left,low,high);root.right = trimBST(root.right,low,high);}//下层逻辑处理完,返回当前节点,便于上层链接return root;}
35、将有序数组转换为二叉搜索树(力扣108)
//1.递归(左闭右开)
// 数组升序,是按照中序遍历二叉搜索树的结果。可选择范围内中间结点作为根结点(其实由数组能生成的二叉搜索树不是唯一的)public TreeNode sortedArrayToBST(int[] nums) {return sorted(nums, 0, nums.length);}public TreeNode sorted(int[] nums, int low, int high) {if (high - low < 1) {return null;}int mid = low + (high - low) / 2;if (high - low == 1) {return new TreeNode(nums[mid]);}TreeNode node = new TreeNode(nums[mid]);node.left = sorted(nums, low, mid);node.right = sorted(nums, mid + 1, high);return node;}
//2.递归(左闭右闭)public TreeNode sortedArrayToBST2(int[] nums) {return sorted(nums, 0, nums.length - 1);}public TreeNode sorted2(int[] nums, int low, int high) {if (low > high) {return null;}int mid = low + (high - low) / 2;TreeNode node = new TreeNode(nums[mid]);node.left = sorted(nums, low, mid - 1);node.right = sorted(nums, mid + 1, high);return node;}
36、把二叉搜索树转换为累加树(力扣538)
//1.递归int number;public TreeNode convertBST(TreeNode root) {number = 0;convert(root);return root;}public void convert(TreeNode root){if(root == null){return;}convert(root.right);number += root.val;root.val = number;convert(root.left);}
//2.迭代int number;public TreeNode convertBST(TreeNode root) {if(root == null) return null;Deque<TreeNode> stack = new LinkedList<>();TreeNode curNode = root;while(curNode != null || !stack.isEmpty()){if(curNode != null){stack.push(curNode);curNode = curNode.right;}else{curNode= stack.pop();number += curNode.val;curNode.val = number;curNode = curNode.left;}}return root;}
37、翻转二叉树(力扣226)
public class Solution {public TreeNode invertTree(TreeNode root) {if (root == null){return root;}/* 交换当前结点的左子树和右子树 */swap(root);/* 向下处理 */invertTree(root.left);invertTree(root.right);return root;}public void swap(TreeNode node){TreeNode tempNode = node.left;node.left = node.right;node.right = tempNode;}}