binary tree Leetcode 二叉树算法题

144.二叉树的前序遍历

前序遍历是:根-左-右

所以记录序列的的时候放在最前面

递归

class Solution {List<Integer> ans = new ArrayList<>();public List<Integer> preorderTraversal(TreeNode root) {if(root == null) return ans;ans.add(root.val);preorderTraversal(root.left);preorderTraversal(root.right);return ans;}
}

迭代

用栈模拟递归

  • 首先访问根节点,存储根节点的值
  • 随后访问左节点,每个左节点又是当前根节点,所以存储当前根节点的值,直到没有左节点就跳出循环
  • 跳出循环后,访问当前节点的右节点,从栈顶取出当前节点,同时由于当前节点的左右节点都被要访问了,所以直接弹出当前节点,然后访问当前节点的右节点
  • 右节点变成了当前根节点,记录值。回到步骤2判断是否有左节点
class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> ans = new ArrayList<>();if(root == null) return ans;Deque<TreeNode> stack = new LinkedList<>();TreeNode node = root;while(node != null || !stack.isEmpty()){while(node != null){ans.add(node.val);stack.push(node);node = node.left;}node = stack.pop();node = node.right;}return ans;}
}

94.二叉树的中序遍历

递归

class Solution {List<Integer> ans = new ArrayList<>();public List<Integer> inorderTraversal(TreeNode root) {if(root == null) return ans;inorderTraversal(root.left);ans.add(root.val);inorderTraversal(root.right);return ans;}
}

迭代

class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> ans = new ArrayList<>();if(root == null) return ans;Deque<TreeNode> stack = new LinkedList<>();TreeNode node = root;while(node != null || !stack.isEmpty()){while(node != null){stack.push(node);node = node.left;}node = stack.pop();ans.add(node.val);node = node.right;}return ans;}
}

145.二叉树的后序遍历

递归

class Solution {List<Integer> ans = new ArrayList<>();public List<Integer> postorderTraversal(TreeNode root) {if(root == null) return ans;postorderTraversal(root.left);postorderTraversal(root.right);ans.add(root.val);return ans;}
}

迭代

后序遍历:左-右-中

  • 遍历完左子树后,需要从栈中获得当前节点,来获得右子树,但是后续还需要遍历中间节点,所以需要加回去栈中
  • 但是当左右子树遍历完,就不需要再加回栈中了,只需要记录中间节点的值了
  • 通过prev记录上一个遍历的右节点,然后判断当前节点的右节点和prev是否指向同一个,就可以判断当前节点是否还需要继续遍历右节点了
class Solution {public List<Integer> postorderTraversal(TreeNode root) {List<Integer> ans = new ArrayList<>();if(root == null) return ans;Deque<TreeNode> stack = new LinkedList<>();TreeNode node = root, prev = null;while(node != null || !stack.isEmpty()){while(node != null){stack.push(node);node = node.left;}node = stack.pop();if(node.right == null || prev == node.right){ans.add(node.val);prev = node;node = null;}else{stack.push(node);node = node.right;}}return ans;}
}

102.二叉树的层序遍历

BFS

class Solution {public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> ans = new ArrayList<>();if(root == null) return ans;Deque<TreeNode> queue = new LinkedList<>();queue.offer(root);while(!queue.isEmpty()){int curLevel = queue.size();List<Integer> ansLevel = new ArrayList<>();for(int i = 0; i < curLevel; ++ i){TreeNode node = queue.poll();ansLevel.add(node.val);if(node.left != null) queue.offer(node.left);if(node.right != null) queue.offer(node.right);}ans.add(ansLevel);}return ans;}
}

我的代码

class Solution {public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> ans = new ArrayList<>();if(root == null) return ans;List<TreeNode> levelNode = new ArrayList<>();levelNode.add(root);while(levelNode.size() != 0){List<Integer> levelAns = new ArrayList<>();List<TreeNode> nextLevelNode = new ArrayList<>();for(TreeNode node : levelNode){levelAns.add(node.val);System.out.println(node.val);if(node.left != null)nextLevelNode.add(node.left);if(node.right != null)nextLevelNode.add(node.right);}levelNode = nextLevelNode;ans.add(levelAns);}return ans;}
}

226.翻转二叉树

递归

class Solution {public TreeNode invertTree(TreeNode root) {if(root == null) return root;TreeNode right = invertTree(root.left);TreeNode left = invertTree(root.right);root.right = right;root.left = left;return root;}
}

101.对称二叉树

根节点的左右子树是对称的,所以让两个子树同时往下遍历

左子树的左节点等于右子树的右节点,反之亦然

并且如果是迭代则让应该相等的节点放在临近的一起

递归

class Solution {public boolean isSymmetric(TreeNode root) {return check(root.left, root.right);}public boolean check(TreeNode l, TreeNode r){if(l == null && r == null) return true;if(l == null || r == null) return false;boolean check1 = check(l.left, r.right);boolean check2 = check(l.right, r.left);return l.val == r.val && check1 && check2;}
}

迭代

注意遍历到叶子节点的时候要 continue

class Solution {public boolean isSymmetric(TreeNode root) {Deque<TreeNode> queue = new LinkedList<>();queue.offer(root.left);queue.offer(root.right);while(!queue.isEmpty()){TreeNode l = queue.poll();TreeNode r = queue.poll();if(l == null && r == null) continue;if(l == null || r == null || l.val != r.val) return false;queue.offer(l.left);queue.offer(r.right);queue.offer(l.right);queue.offer(r.left);}return true;}
}

104.二叉树的最大深度

递归写法:

class Solution {public int maxDepth(TreeNode root) {if(root == null) return 0;int lnum = maxDepth(root.left);int rnum = maxDepth(root.right);return Math.max(lnum, rnum) + 1;}
}

111.二叉树的最小深度

class Solution {public int minDepth(TreeNode root) {if(root == null) return 0;int lmin = minDepth(root.left);int rmin = minDepth(root.right);if(lmin == 0 && rmin == 0) return 1;else if(lmin == 0 || rmin == 0) return Math.max(lmin, rmin)+1;return Math.min(lmin, rmin)+1;}
}

222.完全二叉树的节点个数

完全二叉树的性质

分左右子树遍历:

  • 如果左子树高度和右子树高度相等,则说明左子树已满,所以左子树的节点个数可以计算为 2 l L e v e l 2^{lLevel} 2lLevel,只需要计算右子树节点个数了
  • 如果左子树高度和右子树高度不相等,则右子树已满,所以右子树的节点个数可以计算为 2 r L e v e l 2^{rLevel} 2rLevel,只需要计算左子树节点个数了

img.png

class Solution {public int countNodes(TreeNode root) {if(root == null) return 0;int lLevel = countLevel(root.left);int rLevel = countLevel(root.right);if(lLevel == rLevel){return countNodes(root.right) + (1<<lLevel);}else{return countNodes(root.left) + (1<<rLevel);}}public int countLevel(TreeNode root){int level = 0;while(root != null){++level;root = root.left;}return level;}
}

常规递归遍历

class Solution {public int countNodes(TreeNode root) {if(root == null) return 0;return countNodes(root.left) + countNodes(root.right) + 1;}
}

110.平衡二叉树

自顶向下递归

class Solution {public boolean isBalanced(TreeNode root) {if(root == null) return true;int lLevel = countLevel(root.left);int rLevel = countLevel(root.right);if(Math.abs(lLevel-rLevel) > 1) return false;return isBalanced(root.left) && isBalanced(root.right);}public int countLevel(TreeNode root){if(root == null) return 0;return Math.max(countLevel(root.left), countLevel(root.right)) + 1;}
}

countLevel被重复调用了

自底向上递归

  • 如果平衡就返回高度
  • 如果不平衡就返回一个负数
  • 后面判断是否子树已经不平衡,通过高度是否是负数检测
class Solution {public boolean isBalanced(TreeNode root) {return countLevel(root) >= 0;}public int countLevel(TreeNode root){if(root == null) return 0;int lLevel = countLevel(root.left);int rLevel = countLevel(root.right);if(lLevel == -1 || rLevel == -1 || Math.abs(lLevel-rLevel) > 1){return -1;}return Math.max(lLevel, rLevel) + 1;}
}

左子树已经不平衡,右子树就没必要递归下去了,直接 return

class Solution {public boolean isBalanced(TreeNode root) {return countLevel(root) >= 0;}public int countLevel(TreeNode root){if(root == null) return 0;int lLevel = countLevel(root.left);if(lLevel == -1) return -1;int rLevel = countLevel(root.right);if(rLevel == -1) return -1;if(Math.abs(lLevel-rLevel) > 1){return -1;}return Math.max(lLevel, rLevel) + 1;}
}

257.二叉树的所有路径

遇到叶子节点就把路径计入答案,否则把当前节点计入路径中

class Solution {public List<String> binaryTreePaths(TreeNode root) {List<String> paths = new ArrayList<>();TreePath(root, "", paths);return paths;}public void TreePath(TreeNode root, String path, List<String> paths){if(root == null) return;if(root.left == null && root.right == null){paths.add(path + root.val);return;}path += root.val + "->";TreePath(root.left, path, paths);TreePath(root.right, path, paths);}
}

String 的加法会不断的创建拷贝生成新的字符串,耗费时间和空间。可以用 StringBuilder 或者 StringBuffer 优化

但是它们属于引用类型,后面对其字符串的更改,回溯后更改依然存在,所以在回溯后需要delete掉多余的内容

class Solution {public List<String> binaryTreePaths(TreeNode root) {List<String> paths = new ArrayList<>();TreePath(root, new StringBuilder(), paths);return paths;}public void TreePath(TreeNode root, StringBuilder path, List<String> paths){if(root == null) return;if(root.left == null && root.right == null){paths.add(path.toString() + root.val); // toString 一定在前,+val在后return;}int prevLen = path.length();path.append(root.val).append("->");TreePath(root.left, path, paths);TreePath(root.right, path, paths);path.setLength(prevLen);}
}

404.左叶子之和

https://leetcode.cn/problems/sum-of-left-leaves/

  1. 首先必须有左节点
  2. 然后必须是叶子节点
  3. 每次的递归累计值不能丢失
class Solution {public int sum = 0;public int sumOfLeftLeaves(TreeNode root) {if(root == null) return sum;if(root.left != null && root.left.left == null && root.left.right == null)sum = sum + root.left.val;sumOfLeftLeaves(root.left);sumOfLeftLeaves(root.right);return sum;}
}

513.找树左下角的值

左下角的值,也就是从左往右dfs最深的一层的第一个节点

或者,bfs从左往右的第一个节点(从右往左的最后一个节点)

dfs

记录选择节点的深度

public class Solution {int curDepth = 1;int maxDepth = 0;int curVal = 0;public int findBottomLeftValue(TreeNode root) {if(root == null) return curVal;if(root.left == null && root.right == null){if(maxDepth < curDepth){maxDepth = curDepth;curVal = root.val;return curVal;}}curDepth += 1;findBottomLeftValue(root.left);findBottomLeftValue(root.right);curDepth -= 1;return curVal;}
}

112.路径总和

easy

求二叉树路径上所有节点的和是否等于某个数

每次向下递归的时候减去当前节点的值,直到叶子节点看是否减到0了

左右子树只要有一个返回 true,就存在

class Solution {public boolean hasPathSum(TreeNode root, int targetSum) {if(root == null) return false;if(root.left == null && root.right == null){if(targetSum - root.val == 0) return true;return false;}return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val);}
}

106.从中序与后序遍历序列构造二叉树

中序遍历:左 [中] 右 :获取左右子树的范围

后序遍历:左 右 [中] :获取根节点

所以后序遍历的最后一个节点一定是根节点

  1. 首先根据后序遍历,其最后一个位置就是根节点idx
  2. 找到根节点idx对应中中序遍历的位置 mid ,获取左右子树的范围 [l, mid-1], [mid+1, r]
  3. 递归的在 [l, mid-1], [mid+1, r] 区间重复上述两个过程;
  4. 由于后序遍历是先左,再右,所以下一个根节点也就是–idx是右子树的根节点。所以必须先递归右区间,再递归左区间
class Solution {Map<Integer, Integer> hash;int idx;public TreeNode buildTree(int[] inorder, int[] postorder) {hash = new HashMap<>();for(int i = 0; i < inorder.length; ++ i){hash.put(inorder[i], i);}idx = inorder.length-1;return build(inorder, postorder, 0, inorder.length-1);}public TreeNode build(int[] inorder, int[] postorder, int l, int r){if(l > r){return null;}// if(l == r){//     idx--;//     return new TreeNode(inorder[l]);// }TreeNode root = new TreeNode(postorder[idx]);int mid = hash.get(postorder[idx--]);root.right = build(inorder, postorder, mid + 1, r);root.left = build(inorder, postorder, l, mid-1);return root;}
}

654.最大二叉树

给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:

  • 创建一个根节点,其值为 nums 中的最大值。
  • 递归地在最大值 左边 的 子数组前缀上 构建左子树。
  • 递归地在最大值 右边 的 子数组后缀上 构建右子树。

递归

每次在指定区间内找到最大值作为根节点

class Solution {public TreeNode constructMaximumBinaryTree(int[] nums) {return build(nums, 0, nums.length-1);}public TreeNode build(int[] nums, int l, int r){if(l > r){return null;}int mid = l;for(int i = l + 1; i <= r; ++ i){if(nums[i] > nums[mid]){mid = i;}}TreeNode root = new TreeNode(nums[mid]);root.left = build(nums, l, mid - 1);root.right = build(nums, mid + 1, r);return root;}
}

单调栈

找到每个节点,左边第一个和右边第一个比它大的元素,其中较小的元素为当前节点的父节点

找到每个元素第一个比它大的元素可以用单调栈:

构造一个单调递减的栈,每当加入当前元素:

  • 如果栈顶元素比当前元素小,那么当前元素就是栈顶元素右边第一个比它大的
  • 如果栈顶元素比当前元素大,那么栈顶元素就是当前元素左边第一个比它大的
class Solution {public TreeNode constructMaximumBinaryTree(int[] nums) {int n = nums.length;int[] left = new int[n];int[] right = new int[n];Arrays.fill(left, -1);Arrays.fill(right, -1);Deque<Integer> stack = new ArrayDeque<>();TreeNode[] nodes = new TreeNode[n];for(int i = 0; i < n; ++ i){nodes[i] = new TreeNode(nums[i]);while(!stack.isEmpty() && nums[i] > nums[stack.peek()]){right[stack.pop()] = i;}if(!stack.isEmpty()){left[i] = stack.peek();}stack.push(i);}TreeNode root = null;for(int i = 0; i < n; ++ i){if(left[i] == -1 && right[i] == -1){root = nodes[i];}else if(left[i] == -1 || (right[i] != -1 && nums[left[i]] > nums[right[i]])){nodes[right[i]].left = nodes[i];}else{nodes[left[i]].right = nodes[i];}}return root;}
}

617.合并二叉树

class Solution {public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {if(root1 == null && root2 == null){return null;}int val1 = root1 == null ? 0 : root1.val;int val2 = root2 == null ? 0 : root2.val;TreeNode root = new TreeNode(val1 + val2);root.left = mergeTrees(root1 == null ? null : root1.left, root2 == null ? null : root2.left);root.right = mergeTrees(root1 == null ? null : root1.right, root2 == null ? null : root2.right);return root;}
}

更简介的代码:

不需要判断两个通知为 null 才返回 null

只要有一方为 null,就返回另一方

class Solution {public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {if(root1 == null){return root2;}if(root2 == null){return root1;}TreeNode root = new TreeNode(root1.val + root2.val);root.left = mergeTrees(root1.left, root2.left);root.right = mergeTrees(root1.right, root2.right);return root;}
}

700.二叉搜索树中的搜索

class Solution {public TreeNode searchBST(TreeNode root, int val) {if(root == null){return null;}if(root.val == val){return root;}if(root.val > val){return searchBST(root.left, val);}return searchBST(root.right, val);}
}

98.验证二叉搜索树

递归判断区间范围

class Solution {public boolean isValidBST(TreeNode root) {return check(root, Long.MIN_VALUE, Long.MAX_VALUE);}public boolean check(TreeNode root, long left, long right){if(root == null) return true;if(root.val <= left || root.val >= right) return false;return check(root.left, left, root.val) && check(root.right, root.val, right);}
}

中序遍历

迭代版本的,便于拿到前一个节点的值

530.二叉搜索树的最小绝对差

二叉搜索树的中序遍历结果中,相邻数字相减的最小值

迭代:

class Solution {public int getMinimumDifference(TreeNode root) {Deque<TreeNode> stack = new ArrayDeque<>();int pre = -1, res = Integer.MAX_VALUE;while(root != null || !stack.isEmpty()){while(root != null){stack.push(root);root = root.left;}root = stack.pop();if(pre != -1){res = Math.min(res, root.val - pre);}pre = root.val;root = root.right;}return res;}
}

中序遍历模板:

class Solution{public void minSearch(TreeNode root){Deque<TreeNode> stack = new ArrayDeque<>();// 节点不为空,或者栈里面还有剩下没被遍历的左节点们while(root != null || !stack.isEmpty()){// 中序遍历,先遍历到极左:while(root != null){stack.push(root);root = root.left;}// 中序遍历,极左遍历完了,遍历中间的节点:root = stack.pop(); // 栈中弹出最后遍历到的左节点root = root.rigth; // 最后遍历右节点}}
}

递归:

class Solution {int pre = -1;int ans = Integer.MAX_VALUE;public int getMinimumDifference(TreeNode root) {if(root == null){return ans;}getMinimumDifference(root.left);if(pre != -1){ans = Math.min(ans, root.val - pre);}pre = root.val;getMinimumDifference(root.right);return ans;}
}

236.二叉树的最近公共祖先

dfs

回溯的时候判断,该节点 x 本身及其子节点是否包含p或q中的一个,判断条件:

  • x == p || x == q || lson || rson

  • 如果一个节点 x 的左子树 lson 和 右子树 rson 各自包含一个 p 或者 q 节点,那么 x 就是最近公共祖先

  • 如果一个节点 x 本身是 p 或者 q 节点,x 的 lson 或者 rson 包含剩下的 p 或者 q 节点,那么 x 就是最近公共祖先

转换为:

  • (lson && rson) || ((x == p || x == q) && (lson || rson))
class Solution {TreeNode ans;public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {ans = null;dfs(root, p, q);return ans;}public boolean dfs(TreeNode root, TreeNode p, TreeNode q){if(root == null) return false;boolean lson = dfs(root.left, p, q);boolean rson = dfs(root.right, p, q);if((lson && rson) || ((root.val == p.val || root.val == q.val)&&(lson || rson))){ans = root;return true;}return root.val == p.val || root.val == q.val || lson || rson;}
}

优雅的解法:

class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if (root == null || root == p || root == q) {return root;     // 1)一个是另外一个的祖先}TreeNode left = lowestCommonAncestor(root.left, p, q);TreeNode right = lowestCommonAncestor(root.right, p, q);if (left != null && right != null) {return root;     // 2)左右两边各自有一个o1、o2,返回这个祖先"}return left != null ? left : right;// 1) / 2) 找不到,回溯时一直是null,如果找到了,那么将找到的值往上窜!}
}

存储父节点

从根节点 dfs,存储每个节点的父节点

从 p 开始向上搜索,将遇到的节点进行记录

再次从 q 开始向上搜索,如果遇到了之前遇到的节点,这个节点就是最近公共祖先

235.二叉搜索树的最近公共祖先

一次搜索两个节点

如果 p.val <= root.val <= q.val 那么 root 就是 LCA

class Solution {TreeNode ans = null;public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {ans = null;dfs(root, p, q);return ans;}public void dfs(TreeNode root, TreeNode p, TreeNode q){if(root == null) return;if(root.val > p.val && root.val > q.val) dfs(root.left, p, q);else if(root.val < p.val && root.val < q.val) dfs(root.right, p, q);else ans = root;}
}

两次搜索

两次搜索获取两个目标节点的路径,路径的分叉点就是 LCA

class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {List<TreeNode> path_p = new ArrayList<>();List<TreeNode> path_q = new ArrayList<>();dfs(path_p, root, p);dfs(path_q, root, q);int i = 0;TreeNode ans = null;while(i < path_p.size() && i < path_q.size()){if(path_p.get(i).val == path_q.get(i).val){ans = path_p.get(i);}else break;++i;}return ans;}public void dfs(List<TreeNode> path, TreeNode root, TreeNode target){path.add(root);if(root == target) return;if(root.val > target.val) dfs(path, root.left, target);else dfs(path, root.right, target);}
}

701.二叉搜索树中的插入操作

优雅写法

用递归时的方向直接判断左还是右

返回 root 节点给父节点,重建树

class Solution {boolean add = false;public TreeNode insertIntoBST(TreeNode root, int val) {return dfs(root, val);}public TreeNode dfs(TreeNode root, int val){if(root == null) return new TreeNode(val);if(val < root.val){root.left = dfs(root.left, val);}else{root.right = dfs(root.right, val);}return root;}
}

我的代码

找到插入的叶子节点,判断插入的方向。插入后限制不可插入:

class Solution {boolean add = false;TreeNode node = null;public TreeNode insertIntoBST(TreeNode root, int val) {node = new TreeNode(val);dfs(root, val);return root == null ? node : root;}public void dfs(TreeNode root, int val){if(root == null) return;int dir = 0;if(root.val > val) {dfs(root.left, val);dir = 1;}else dfs(root.right, val);if(add == false){if(dir == 0){root.right = node;}else{root.left = node;}add = true;}}
}

450.删除二叉搜索树中的节点

首先找到要删除的节点,然后分类讨论:

  • 如果删除节点左右子树为空,则直接返回 null
  • 如果删除节点左子树为空,返回右子树
  • 如果删除节点右子树为空,返回左子树
  • 如果删除节点左右子树都不为空:
    • 找到删除节点的后继节点,也就是 delete 节点的右子树的最左节点 node
    • 该 node 节点是仅仅只大于 delete节点的,所以用 node 代替 delete 节点,二叉树的性质保持不变
    • 替代方法:首先删除 node 节点,然后改变 node 的左右子树
class Solution {public TreeNode deleteNode(TreeNode root, int key) {if(root == null) return root;if(key < root.val){root.left = deleteNode(root.left, key);return root;}else if(key > root.val){root.right = deleteNode(root.right, key);return root;}else if(key == root.val){if(root.left == null && root.right == null){return null;}else if(root.left == null){return root.right;}else if(root.right == null){return root.left;}else{TreeNode node = root.right;while(node.left != null){node = node.left;}// 这里要写 root.right = deleteNode(xx),因为有可能删除的是root.right,就会返回 nullroot.right = deleteNode(root.right, node.val);node.left = root.left;node.right = root.right;return node;}}return root;}
}

696.修剪二叉搜索树

将不在区间 [low, high] 内的节点剪掉

  • 如果节点的值 小于 low ,则只用修剪右子树

  • 如果节点的值 大于 high,则只用修建左子树

  • 如果位于区间,则其左右子树可能不正常,所以递归修建左右子树

  • 修剪到最后,如果没有正常的节点则会一直递归到节点为 null,返回 null

  • 如果遇到正常节点,则返回该节点本身

class Solution {public TreeNode trimBST(TreeNode root, int low, int high) {if(root == null) return root;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;}}
}

108.将有序数组转换为二叉搜索树

每次选择区间内的中间值作为根节点,递归构造左右子树,

直到区间唯一个点返回该点构造的节点,或者 l > r 说明不存在左边或者右边的值,返回 null

返回左右子树构造好的根节点给上层的根节点构造左右子树

class Solution {public TreeNode sortedArrayToBST(int[] nums) {return build(nums, 0, nums.length - 1);}public TreeNode build(int[] nums, int l, int r){if(l == r){return new TreeNode(nums[l]);}else 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;}
}

538.把二叉搜索树转换为累加树

反向中序遍历,累加之前的值:

class Solution {int sum = 0;public TreeNode convertBST(TreeNode root) {if(root == null) return root;convertBST(root.right);sum += root.val;root.val = sum;convertBST(root.left);return root;}
}

我的代码:

先dfs一遍算出总和

中序遍历一遍,减去之前的值

class Solution {int sum = 0;public TreeNode convertBST(TreeNode root) {dfs(root);if(root != null) sum += root.val;build(root);return root;}public int dfs(TreeNode root){if(root == null) return 0;int l = dfs(root.left);int r = dfs(root.right);sum = sum + l + r;return root.val;}public void build(TreeNode root){if(root == null) return;build(root.left);int sub = root.val;root.val = sum;sum -= sub;build(root.right);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/1244.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【HCIP】OSPF的高级特性

OSPF的高级特性1 --- 不规则区域 一、OSPF不规则区域类型 产生原因&#xff1a;区域划分不合理&#xff0c;导致的问题 1、非骨干区域无法和骨干区域保持连通 2、骨干区域被分割 造成后果&#xff1a;非骨干区域没和骨干区域相连&#xff0c;导致ABR将不会帮忙转发区域间的路由…

【数据结构练习题】堆——top-k问题

♥♥♥♥♥个人主页♥♥♥♥♥ ♥♥♥♥♥数据结构练习题总结专栏♥♥♥♥♥ ♥♥♥♥♥上一章&#xff1a;【数据结构练习题】二叉树(1)——1.相同的树2.另一颗树的子树3.翻转二叉树4.平衡二叉树5.对称二叉树♥♥♥♥♥ 文章目录 1.top-k问题1.1问题描述1.2思路分析1.3绘图分析…

理光打印机设置扫描文件到共享文件夹教程(线上和现场)

在线设置。 1.点击用户工具/计数器按钮。 2.点击系统设置。 3.点击端口设置&#xff0c;点击机器IPV4地址。 4.获得打印机IP地址。 5.回到共享电脑&#xff0c;新建一个账户或者使用当前账户&#xff0c;为了隐私安全起见&#xff0c;最好设置密码。 6.关闭防火墙。 7.启用…

Mac 下安装PostgreSQL经验

使用homebrew终端软件管理器去安装PostgreSQL 如果没有安装brew命令执行以下命令 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 沙果开源物联网系统 SagooIoT | SagooIoT 1.使用命令安装postgreSQL brew i…

JavaScript之分时函数、分时间段渲染页面、提高用户体验、参数归一化、高阶函数、分段、appendChild、requestIdleCallback

MENU 前言效果图html原始写法优化方式一(参数归一化)优化方式二(当浏览器不支持requestIdleCallback方法的时候)优化方式三(判断环境) 前言 当前需要向页面插入十万个div元素&#xff0c;如果使用普通的渲染方式&#xff0c;会造成延迟。这时候就需要通过分时函数来实现渲染了。…

【Pytorch】VSCode实用技巧 - 默认终端修改为conda activate pytorch

VScode修改配置使得启动终端为conda环境 文章目录 VScode修改配置使得启动终端为conda环境1、找到settings.json 文件2、查找 conda / mamba 相关内容3、编辑 settings.json 文件4、异常处理5、补充检验 VScode跑项目&#xff0c;在启动pytorch项目时往往会有千奇百怪的问题&am…

大学生前端学习第一天:了解前端

引言&#xff1a; 哈喽&#xff0c;各位大学生们&#xff0c;大家好呀&#xff0c;在本篇博客&#xff0c;我们将引入一个新的板块学习&#xff0c;那就是前端&#xff0c;关于前端&#xff0c;GPT是这样描述的&#xff1a;前端通常指的是Web开发中用户界面的部分&#xff0c;…

数据库设计的三范式

简单来说就是&#xff1a;原子性、唯一性、独立性 后一范式都是在前一范式已经满足的情况进行附加的内容 第一范式&#xff08;1NF&#xff09;&#xff1a;原子性 存储的数据应不可再分。 不满足原子性&#xff1a; 满足原子性&#xff1a; 第二范式&#xff08;2NF&#xf…

探索设计模式的魅力:开启智慧之旅,AI与机器学习驱动的微服务设计模式探索

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 ✨欢迎加入探索AI与机器学习驱动的微服务设计模式之旅✨ 亲爱的科技爱好者们&#xff0c;有没…

LabVIEW多设备控制与数据采集系统

LabVIEW多设备控制与数据采集系统 随着科技的进步&#xff0c;自动化测试与控制系统在工业、科研等领域的应用越来越广泛。开发了一种基于LabVIEW平台开发的多设备控制与数据采集系统&#xff0c;旨在解决多设备手动设置复杂、多路数据显示不直观、数据存储不便等问题。通过RS…

【记录】Python3|Selenium 下载 PDF 不预览不弹窗(2024年)

版本&#xff1a; Chrome 124Python 3.12Selenium 4.19.0 版本与我有差异不要紧&#xff0c;只要别差异太大比如 Chrome 用 57 之前的版本了&#xff0c;就可以看本文。 如果你从前完全没使用过、没安装过Selenium&#xff0c;可以参考这篇博客《【记录】Python3&#xff5c;Se…

密码学 | 承诺:绑定性 + 隐藏性

&#x1f951;原文&#xff1a;承诺方案&#xff08;Commitment&#xff09;学习笔记 &#x1f951;写在前面&#xff1a; 本文属搬运博客&#xff0c;自己留存学习。本文只会讲承诺的两个安全属性&#xff0c;不会再讲解承诺的定义。 正文 承诺方案需要满足两个安全属性&…

Oracle之SQL plus的一些经验心得

每次登入SQL plus后,不知道时哪个用户登入,非常不方便,只能使用show user查看。 以下时可以通过一些设置实现上述的效果,知道时哪个用户登入,和实现输出效果等 1)SQL plus使用细则 SQL plus登录时,我们可以设置一些通用的设置,在每次登入SQL plus的时候生效。 [root@c…

安装Zipkin

官网&#xff1a;https://zipkin.io/pages/quickstart.html Jar包方式 下载 方式一&#xff1a;百度网盘下载 链接&#xff1a;https://pan.baidu.com/s/1PRV1RamJ8IWX32IJb7jw3Q?pwde8vu 提取码&#xff1a;e8vu 方式二&#xff1a;Central Repository: io/zipkin/zipk…

react 项目路由配置(react-router-dom 版本 v6.3、v6.4)

根据 react-router-dom 的版本&#xff0c;有不同的方式 一、react-router-dom v6.3 用到的主要 api: BrowserRouteruseRoutesOutlet 下面是详细步骤&#xff1a; 1、index.js BrowserRouter 用来实现 单页的客户端路由使用 BrowserRouter 包裹 App放在 顶级 位置&#x…

(1)认识人工智能

第一章 认识人工智能 引言 本人目前大三&#xff0c;双非一本的人工智能专业&#xff0c;代码能力不算太差&#xff0c;做过项目&#xff0c;也打了比赛&#xff0c;获了奖&#xff0c;但是走技术路线总会有否定自己的感觉&#xff0c;可能是感觉自己的才能没有在搞技术方面实…

小红书电商运营实战课,从0打造全程实操(65节视频课)

课程内容&#xff1a; 1.小红书的电商介绍 .mp4 2.小红书的开店流程,mp4 3.小红书店铺基础设置介绍 ,mp4 4.小红书店铺产品上架流程 .mp4 5.客服的聊天过程和子账号建立 .mp4 6.店铺营销工具使用和后台活动参加 .mp4 7.小红书产品上架以及拍单教程,mp4 8.小红书如何选品…

数据分析_商品维度占比及变化可视化分析(Pandas和Matplotlib)

数据分析_商品维度占比及变化可视化分析(Pandas和Matplotlib) 分析维度包括: 各商品年度销量占比 各商品月度销量变化 构建测试数据 这里你可以了解到: 如何生成时间相关的数据。 如何从列表&#xff08;可迭代对象&#xff09;中生成随机数据。 Pandas 的 DataFrame 自…

【JVM系列】关于静态块、静态属性、构造块、构造方法的执行顺序

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

2.Vue简介

Vue简介 Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。无论是简单还是复杂的界面&#xff0c;V…