110.平衡二叉树
题目链接:110. 平衡二叉树 - 力扣(LeetCode)
题目
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
- 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
示例 1:
输入:root = [3,9,20,null,null,15,7] 输出:true
示例 2:
输入:root = [1,2,2,3,3,null,null,4,4] 输出:false
示例 3:
输入:root = [] 输出:true
思路
递归:
- 分别求左子树和右子树高度,计算高度差;
- 如果高度差大于1,记当前树的高度为-1;否则,当前树的高度是max(左子树高度,右子树高度) + 1;
- 当然,如果求出的左子树或右子树的高度已经为-1,直接返回-1即可(底层非平衡二叉树,结果会一直传给上层);
- 最终返回整棵树高度是否为-1的bool值,即是否为平衡二叉树;
迭代: 可以先定义一个函数,专门用来求高度。
这个函数通过栈模拟的后序遍历找每一个节点的高度(其实是通过求传入节点为根节点的最大深度来求的高度)
代码
1 class Solution { 2 /** 3 * 递归法 4 */ 5 public boolean isBalanced(TreeNode root) { 6 return getHeight(root) != -1; 7 } 8 9 private int getHeight(TreeNode root) { 10 if (root == null) { 11 return 0; 12 } 13 int leftHeight = getHeight(root.left); 14 if (leftHeight == -1) { 15 return -1; 16 } 17 int rightHeight = getHeight(root.right); 18 if (rightHeight == -1) { 19 return -1; 20 } 21 // 左右子树高度差大于1,return -1表示已经不是平衡树了 22 if (Math.abs(leftHeight - rightHeight) > 1) { 23 return -1; 24 } 25 return Math.max(leftHeight, rightHeight) + 1; 26 } 27 } 28 29 class Solution { 30 /** 31 * 迭代法,效率较低,计算高度时会重复遍历 32 * 时间复杂度:O(n^2) 33 */ 34 public boolean isBalanced(TreeNode root) { 35 if (root == null) { 36 return true; 37 } 38 Stack<TreeNode> stack = new Stack<>(); 39 TreeNode pre = null; 40 while (root!= null || !stack.isEmpty()) { 41 while (root != null) { 42 stack.push(root); 43 root = root.left; 44 } 45 TreeNode inNode = stack.peek(); 46 // 右结点为null或已经遍历过 47 if (inNode.right == null || inNode.right == pre) { 48 // 比较左右子树的高度差,输出 49 if (Math.abs(getHeight(inNode.left) - getHeight(inNode.right)) > 1) { 50 return false; 51 } 52 stack.pop(); 53 pre = inNode; 54 root = null;// 当前结点下,没有要遍历的结点了 55 } else { 56 root = inNode.right;// 右结点还没遍历,遍历右结点 57 } 58 } 59 return true; 60 } 61 62 /** 63 * 层序遍历,求结点的高度 64 */ 65 public int getHeight(TreeNode root) { 66 if (root == null) { 67 return 0; 68 } 69 Deque<TreeNode> deque = new LinkedList<>(); 70 deque.offer(root); 71 int depth = 0; 72 while (!deque.isEmpty()) { 73 int size = deque.size(); 74 depth++; 75 for (int i = 0; i < size; i++) { 76 TreeNode poll = deque.poll(); 77 if (poll.left != null) { 78 deque.offer(poll.left); 79 } 80 if (poll.right != null) { 81 deque.offer(poll.right); 82 } 83 } 84 } 85 return depth; 86 } 87 } 88 89 class Solution { 90 /** 91 * 优化迭代法,针对暴力迭代法的getHeight方法做优化,利用TreeNode.val来保存当前结点的高度,这样就不会有重复遍历 92 * 获取高度算法时间复杂度可以降到O(1),总的时间复杂度降为O(n)。 93 * 时间复杂度:O(n) 94 */ 95 public boolean isBalanced(TreeNode root) { 96 if (root == null) { 97 return true; 98 } 99 Stack<TreeNode> stack = new Stack<>(); 100 TreeNode pre = null; 101 while (root != null || !stack.isEmpty()) { 102 while (root != null) { 103 stack.push(root); 104 root = root.left; 105 } 106 TreeNode inNode = stack.peek(); 107 // 右结点为null或已经遍历过 108 if (inNode.right == null || inNode.right == pre) { 109 // 输出 110 if (Math.abs(getHeight(inNode.left) - getHeight(inNode.right)) > 1) { 111 return false; 112 } 113 stack.pop(); 114 pre = inNode; 115 root = null;// 当前结点下,没有要遍历的结点了 116 } else { 117 root = inNode.right;// 右结点还没遍历,遍历右结点 118 } 119 } 120 return true; 121 } 122 123 /** 124 * 求结点的高度 125 */ 126 public int getHeight(TreeNode root) { 127 if (root == null) { 128 return 0; 129 } 130 int leftHeight = root.left != null ? root.left.val : 0; 131 int rightHeight = root.right != null ? root.right.val : 0; 132 int height = Math.max(leftHeight, rightHeight) + 1; 133 root.val = height;// 用TreeNode.val来保存当前结点的高度 134 return height; 135 } 136 }
总结
要区分二叉树 深度 和 高度 的概念。
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。
求深度可以从上到下去查 所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中)。
如果求的是二叉树的最大深度,也可以用后序遍历。
因为代码的逻辑其实是求的根节点的高度,而根节点的高度就是这棵树的最大深度,所以才可以使用后序遍历。
257. 二叉树的所有路径
题目链接:257. 二叉树的所有路径 - 力扣(LeetCode)
题目
给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [1,2,3,null,5] 输出:["1->2->5","1->3"]
示例 2:
输入:root = [1] 输出:["1"]
思路
递归法:这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。
在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一个路径再进入另一个路径。
迭代法:可以使用两个栈(也可以用一个)来解决。
代码
1 //解法一 2 class Solution { 3 /** 4 * 递归法 5 */ 6 public List<String> binaryTreePaths(TreeNode root) { 7 List<String> res = new ArrayList<>();// 存最终的结果 8 if (root == null) { 9 return res; 10 } 11 List<Integer> paths = new ArrayList<>();// 作为结果中的路径 12 traversal(root, paths, res); 13 return res; 14 } 15 16 private void traversal(TreeNode root, List<Integer> paths, List<String> res) { 17 paths.add(root.val);// 前序遍历,中 18 // 遇到叶子结点 19 if (root.left == null && root.right == null) { 20 // 输出 21 StringBuilder sb = new StringBuilder();// StringBuilder用来拼接字符串,速度更快 22 for (int i = 0; i < paths.size() - 1; i++) { 23 sb.append(paths.get(i)).append("->"); 24 } 25 sb.append(paths.get(paths.size() - 1));// 记录最后一个节点 26 res.add(sb.toString());// 收集一个路径 27 return; 28 } 29 // 递归和回溯是同时进行,所以要放在同一个花括号里 30 if (root.left != null) { // 左 31 traversal(root.left, paths, res); 32 paths.remove(paths.size() - 1);// 回溯 33 } 34 if (root.right != null) { // 右 35 traversal(root.right, paths, res); 36 paths.remove(paths.size() - 1);// 回溯 37 } 38 } 39 }
1 // 解法2 2 class Solution { 3 /** 4 * 迭代法 5 */ 6 public List<String> binaryTreePaths(TreeNode root) { 7 List<String> result = new ArrayList<>(); 8 if (root == null) 9 return result; 10 Stack<Object> stack = new Stack<>(); 11 // 节点和路径同时入栈 12 stack.push(root); 13 stack.push(root.val + ""); 14 while (!stack.isEmpty()) { 15 // 节点和路径同时出栈 16 String path = (String) stack.pop(); 17 TreeNode node = (TreeNode) stack.pop(); 18 // 若找到叶子节点 19 if (node.left == null && node.right == null) { 20 result.add(path); 21 } 22 //右子节点不为空 23 if (node.right != null) { 24 stack.push(node.right); 25 stack.push(path + "->" + node.right.val); 26 } 27 //左子节点不为空 28 if (node.left != null) { 29 stack.push(node.left); 30 stack.push(path + "->" + node.left.val); 31 } 32 } 33 return result; 34 } 35 }
总结
使用两个list来存数据,一个存所经历路径的值,另一个存结果可以更方便的得出结果。
另外一个就是我们进行递归的时候要知道每次都有回溯的过程,把回溯写在递归后,就能使每次存的值减少。
404.左叶子之和
题目链接:404. 左叶子之和 - 力扣(LeetCode)
题目
给定二叉树的根节点 root ,返回所有左叶子之和。
示例 1:
输入: root = [3,9,20,null,null,15,7] 输出: 24 解释: 在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24
示例 2:
输入: root = [1] 输出: 0
思路
首先要明确左叶子的定义:节点A的左孩子不为空,且左孩子的左右孩子都为空(说明是叶子节点),那么A节点的左孩子为左叶子节点。
递归法:其次左孩子的定义后,就可以确定遍历顺序为后序遍历(左右中),因为要通过递归函数的返回值来累加求取左叶子数值之和。
迭代法:本题迭代法使用前中后序都是可以的,只要把左叶子节点统计出来,就可以了。
代码
1 // 迭代 2 class Solution { 3 public int sumOfLeftLeaves(TreeNode root) { 4 if (root == null) return 0; 5 int leftValue = sumOfLeftLeaves(root.left); // 左 6 int rightValue = sumOfLeftLeaves(root.right); // 右 7 8 int midValue = 0; 9 if (root.left != null && root.left.left == null && root.left.right == null) { 10 midValue = root.left.val; 11 } 12 int sum = midValue + leftValue + rightValue; // 中 13 return sum; 14 } 15 }
1 class Solution { 2 public int sumOfLeftLeaves(TreeNode root) { 3 if (root == null) return 0; 4 Stack<TreeNode> stack = new Stack<> (); 5 stack.add(root); 6 int result = 0; 7 while (!stack.isEmpty()) { 8 TreeNode node = stack.pop(); 9 if (node.left != null && node.left.left == null && node.left.right == null) { 10 result += node.left.val; 11 } 12 if (node.right != null) stack.add(node.right); 13 if (node.left != null) stack.add(node.left); 14 } 15 return result; 16 } 17 }
1 // 层序遍历迭代法 2 class Solution { 3 public int sumOfLeftLeaves(TreeNode root) { 4 int sum = 0; 5 if (root == null) return 0; 6 Queue<TreeNode> queue = new LinkedList<>(); 7 queue.offer(root); 8 while (!queue.isEmpty()) { 9 int size = queue.size(); 10 while (size -- > 0) { 11 TreeNode node = queue.poll(); 12 if (node.left != null) { // 左节点不为空 13 queue.offer(node.left); 14 if (node.left.left == null && node.left.right == null){ // 左叶子节点 15 sum += node.left.val; 16 } 17 } 18 if (node.right != null) queue.offer(node.right); 19 } 20 } 21 return sum; 22 } 23 }
总结
本题的关键就是找到需要的结点,也就是我们的目标孩子的父节点。
标签:right,return,随想录,404,二叉树,null,root,节点,left From: https://www.cnblogs.com/xpp3/p/17136072.html