算法-二叉树
1. 二叉树的递归遍历(LeetCode 144, 145, 94)
递归的进行先序、后序、中序遍历
/**
* 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> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
preorder(root, result);
return result;
}
public void preorder(TreeNode root, List<Integer> result) {
if(root == null)
return;
result.add(root.val);
preorder(root.left, result);
preorder(root.right, result);
return;
}
}
// 后序
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
postorder(root, result);
return result;
}
public void postorder(TreeNode root, List<Integer> result){
if(root == null)
return;
postorder(root.left, result);
postorder(root.right, result);
result.add(root.val);
}
}
// 中序
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
inorder(root, result);
return result;
}
public void inorder(TreeNode root, List<Integer> result){
if(root == null)
return;
inorder(root.left, result);
result.add(root.val);
inorder(root.right, result);
return ;
}
}
2. 二叉树的非递归遍历(LeetCode 94, 145, 144)
2.1 前序遍历
class Solution {
// 先序非递归遍历 根-左-右
// 压栈的时候要让右节点先进入栈
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
if(root != null)
stack.push(root);
while(!stack.isEmpty()){
TreeNode node = stack.pop();
result.add(node.val);
if(node.right != null)
stack.push(node.right);
if(node.left != null)
stack.push(node.left);
}
return result;
}
}
2.2 后序遍历
在前序遍历的基础上修改
- 前序:根左右
- 后序:根左右(翻转左右) -> 根右左(翻转result) -> 左右根
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
Stack<TreeNode> stack = new Stack<>();
if(root != null)
stack.push(root);
while(!stack.empty()){
TreeNode node = stack.pop();
result.add(node.val);
if(node.left != null)
stack.push(node.left);
if(node.right != null)
stack.push(node.right);
}
reverse(result);
return result;
}
// 翻转数组
// 注意List的处理方式和数组不同,需要通过length(), get(index), set(index, value)等方法
public void reverse(List<Integer> result){
int len = result.size();
for(int i = 0; i<len/2 ; ++i){
int temp = result.get(i);
result.set(i, result.get(len-1-i));
result.set(len-1-i, temp);
}
}
}
2.3 中序遍历(有难度)
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while(cur != null || !stack.empty()){
// 一直走到树的最底层
if(cur != null){
stack.push(cur);
cur = cur.left;
} else {
cur = stack.pop();
result.add(cur.val);
cur = cur.right;
}
}
return result;
}
}
3. 二叉树的层序遍历(LeetCode 102, 107, 199, 637, 429, 515, 116, 117, 104, 111)
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
因为需要逐层输出二叉树的节点,所以在访问每层的第一个节点前,需要记录该层的节点数量(等于此时队列的长度)levelNodeNum
。
因为访问完上一层的最后一个节点时,队列中保存的恰是下一层的所有节点。
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
List<List<Integer>> result = new ArrayList<List<Integer>>();
if(root != null)
queue.offer(root);
while(!queue.isEmpty()){
// 记录每层节点的数量
int levelNodeNum = queue.size();
// 记录每层的节点值
List<Integer> levelResult = new ArrayList<Integer>();
while(levelNodeNum > 0){
TreeNode node = queue.poll();
levelNodeNum--; // 该层还需要遍历的节点数量减一
levelResult.add(node.val);
if(node.left != null)
queue.offer(node.left);
if(node.right != null)
queue.offer(node.right);
}
// 结束一层遍历的收尾操作
result.add(levelResult);
}
return result;
}
}
4. 对称二叉树(LeetCode 101)
给你一个二叉树的根节点 root , 检查它是否轴对称。
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null)
return true;
return compareNode(root.left, root.right);
}
public boolean compareNode(TreeNode left, TreeNode right) {
if(left == null && right == null)
return true;
if(left == null && right != null)
return false;
if(left != null && right == null)
return false;
if(left.val != right.val)
return false;
// 外侧和外侧比,内侧和内侧比
boolean outside = compareNode(left.left, right.right);
boolean inside = compareNode(left.right, right.left);
return outside && inside;
}
}
5. 完全二叉树的节点个数(LeetCode 222)
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树 的定义如下:
在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。
若最底层为第 h 层,则该层包含 1~ 2h 个节点。
- 性质:在完全二叉树的前提下:满二叉树的最左深度和最右深度是相等的。
- Math.pow(a, b)的返回值是double类型,可能会出现未知的问题
- 如果要计算2的幂次,建议使用移位的方式
2 << n
class Solution {
// 时间复杂度(lgn * lgn)
public int countNodes(TreeNode root) {
if(root == null)
return 0;
int leftDepth = 0;
int rightDepth = 0;
TreeNode leftTemp = root.left;
TreeNode rightTemp = root.right;
while(leftTemp != null){
leftDepth++;
leftTemp = leftTemp.left;
}
while(rightTemp != null){
rightDepth++;
rightTemp = rightTemp.right;
}
// 如果左右子树的外侧深度相同,则以该节点为根的完全二叉树是满二叉树
if(leftDepth == rightDepth){
return (2 << leftDepth) - 1;
}
return countNodes(root.left) + countNodes(root.right) + 1;
}
}
6. 平衡二叉树(LeetCode 110) (有难度)
给定一个二叉树,判断它是否是平衡二叉树
(所有节点的左右子树的深度相差不超过1)
- 每次计算左右节点的高度时都需要进行一次检查;如果绝对差值的大于1,则递归返回。
class Solution {
public boolean isBalanced(TreeNode root) {
return (getHeight(root) != -1);
}
// 如果不是平衡二叉树,则返回-1
// 一旦发现有非平衡的子树,则开始递归返回。
public int getHeight(TreeNode root) {
if(root == null)
return 0;
int leftHeight = getHeight(root.left);
if(leftHeight == -1){
return -1;
}
int rightHeight = getHeight(root.right);
if(rightHeight == -1){
return -1;
}
if(Math.abs(leftHeight - rightHeight) > 1) {
return -1;
}
return Math.max(leftHeight, rightHeight) + 1;
}
}
7. 二叉树的所有路径(LeetCode 257)
给你一个二叉树的根节点 root ,按任意顺序,返回所有从根节点到叶子节点的路径。
示例:
输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
List<String> result = new ArrayList<String>();
Stack<TreeNode> stack = new Stack<TreeNode>();
preorderTraverse(root, stack, result);
return result;
}
public void preorderTraverse(TreeNode root, Stack<TreeNode> stack, List<String> result){
if(root == null)
return ;
stack.push(root);
// 访问到叶子节点,将栈中的节点打印出来
if(root.left == null && root.right == null) {
String path = new String();
for(int i = 0; i < stack.size(); ++i){
path += new String(Integer.toString(stack.get(i).val));
if(stack.size() == 1 || i == stack.size() - 1) {}
else path += "->";
}
stack.pop();
result.add(path);
return ;
}
preorderTraverse(root.left, stack, result);
preorderTraverse(root.right, stack, result);
// 左右孩子都访问完了,需要弹出该节点
stack.pop();
return ;
}
}
8. 左叶子之和(LeetCode 404)
给定二叉树的根节点 root ,返回所有左叶子之和。
关键是如何判定左叶子。
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
if(root == null)
return 0;
int leftValue = sumOfLeftLeaves(root.left);
// 左子树是叶子
if(root.left != null && root.left.left == null && root.left.right == null)
leftValue = root.left.val;
int rightValue = sumOfLeftLeaves(root.right);
return leftValue + rightValue;
}
}
9. 找树左下角的值(LeetCode 513)
给定一个二叉树的 根节点 root,请找出该二叉树的最底层、最左边
节点的值。
假设二叉树中至少有一个节点。
class Solution {
// 层序遍历
public int findBottomLeftValue(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<TreeNode>();
int result = root.val;
int levelNodeNum = 0;
queue.offer(root);
while(!queue.isEmpty()){
levelNodeNum = queue.size();
for(int i = 0; i<levelNodeNum; ++i){
if(i == 0)
result = queue.peek().val;
TreeNode temp = queue.poll();
if(temp.left != null)
queue.offer(temp.left);
if(temp.right != null)
queue.offer(temp.right);
}
}
return result;
}
}
10. 路径总和(LeetCode 112)
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。
判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。
如果存在,返回 true ;否则,返回 false 。
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root == null)
return false;
if(root.left == null && root.right == null)
return (targetSum == root.val);
boolean leftFlag = hasPathSum(root.left, targetSum - root.val);
boolean rightFlag = hasPathSum(root.right, targetSum - root.val);
return (leftFlag || rightFlag);
}
}
11. 路径总和 II(LeetCode 113)
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
注意:
- 添加到result中的应为curPath的复制体,否则已添加的result元素会随着curPath的改变而改变!
class Solution {
List<List<Integer>> result = new ArrayList<List<Integer>>();
LinkedList<Integer> curPath = new LinkedList<Integer>();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
visit(root, targetSum);
return result;
}
public void visit(TreeNode root, int targetSum){
if(root == null)
return ;
curPath.offer(root.val);
if(root.left == null && root.right == null){
if(root.val == targetSum) {
// 应该复制一份添加到result中
// 否则result中的元素会被篡改
result.add(new LinkedList<>(curPath));
}
curPath.pollLast();
return ;
}
visit(root.left, targetSum - root.val);
visit(root.right, targetSum - root.val);
curPath.pollLast();
return ;
}
}
12. 从中序与后序遍历序列构造二叉树(LeetCode 106) (有难度)
给定两个整数数组inorder
和postorder
,
其中inorder
是二叉树的中序遍历,postorder
是同一棵树的后序遍历,请你构造并返回这颗二叉树 。
关键:
- 对同一棵子树,后序序列和中序序列的长度是一样的。
- 数组的边界处理要仔细
class Solution {
// 1. 获取后续遍历的最后一个元素
// 2. 切分中序遍历数组
// 3. 切分后序遍历数组
// 关键:后序遍历数组的切分长度和中序遍历序列的长度一样
public TreeNode buildTree(int[] inorder, int[] postorder) {
TreeNode root = buildTree(inorder, postorder,
0, inorder.length - 1,
0, postorder.length - 1);
return root;
}
public TreeNode buildTree(
int[] inorder, int[] postorder,
int inorderStartIndex, int inorderEndIndex,
int postorderStartIndex, int postorderEndIndex)
{
if(inorderStartIndex > inorderEndIndex)
return null;
if(inorderStartIndex == inorderEndIndex)
return new TreeNode(inorder[inorderStartIndex]);
int rootValue = postorder[postorderEndIndex];
TreeNode root = new TreeNode(rootValue);
int inoderDivideIndex = inorderStartIndex;
for(inoderDivideIndex = inorderStartIndex; inoderDivideIndex < inorderEndIndex; inoderDivideIndex++){
if(inorder[inoderDivideIndex] == rootValue)
break;
}
root.left = buildTree(inorder, postorder,
inorderStartIndex, inoderDivideIndex - 1,
postorderStartIndex,
postorderStartIndex + (inoderDivideIndex - inorderStartIndex) - 1
);
root.right = buildTree(inorder, postorder,
inoderDivideIndex + 1, inorderEndIndex,
postorderStartIndex + (inoderDivideIndex - inorderStartIndex),
postorderEndIndex - 1
);
return root;
}
}
13. 从前序与中序遍历序列构造二叉树(LeetCode 105)
思路和上一题类似
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder.length == 0)
return null;
TreeNode root = buildTree(preorder, inorder, 0, preorder.length-1, 0, inorder.length - 1);
return root;
}
public TreeNode buildTree(int[] preorder, int[] inorder,
int preStart, int preEnd,
int inStart, int inEnd
) {
if(preStart > preEnd)
return null;
if(preStart == preEnd)
return new TreeNode(inorder[inStart]);
int rootVal = preorder[preStart];
TreeNode root = new TreeNode(rootVal);
int inDivide = inStart;
for(;inDivide < inEnd; ++inDivide){
if(inorder[inDivide] == rootVal)
break;
}
int leftLength = inDivide - inStart;
root.left = buildTree(preorder, inorder,
preStart + 1, preStart + leftLength,
inStart, inStart + leftLength - 1
);
root.right = buildTree(preorder, inorder,
preStart + leftLength + 1, preEnd,
inDivide + 1, inEnd
);
return root;
}
}
14. 构造最大二叉树(LeetCode 654)
给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:
- 创建一个根节点,其值为 nums 中的最大值。
- 递归地在最大值 左边 的 子数组前缀上 构建左子树。
- 递归地在最大值 右边 的 子数组后缀上 构建右子树。
- 返回 nums 构建的 最大二叉树 。
输入:nums = [3,2,1,6,0,5]
输出:[6,3,5,null,2,0,null,null,1]
解释:递归调用如下所示:
- [3,2,1,6,0,5] 中的最大值是 6 ,左边部分是 [3,2,1] ,右边部分是 [0,5] 。
- [3,2,1] 中的最大值是 3 ,左边部分是 [] ,右边部分是 [2,1] 。
- 空数组,无子节点。
- [2,1] 中的最大值是 2 ,左边部分是 [] ,右边部分是 [1] 。
- 空数组,无子节点。
- 只有一个元素,所以子节点是一个值为 1 的节点。
- [0,5] 中的最大值是 5 ,左边部分是 [0] ,右边部分是 [] 。
- 只有一个元素,所以子节点是一个值为 0 的节点。
- 空数组,无子节点。
class Solution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
if(nums.length == 0)
return null;
TreeNode root = constructMaximumBinaryTree(nums, 0, nums.length-1);
return root;
}
public TreeNode constructMaximumBinaryTree(int[] nums, int start, int end){
if(start > end)
return null;
if(start == end)
return new TreeNode(nums[start]);
int max = nums[start];
int maxIndex = start;
for(int i = start; i <= end; ++i){
if(nums[i] > max){
max = nums[i];
maxIndex = i;
}
}
TreeNode root = new TreeNode(max);
root.left = constructMaximumBinaryTree(nums, start, maxIndex-1);
root.right = constructMaximumBinaryTree(nums, maxIndex + 1, end);
return root;
}
}
15. 合并二叉树(LeetCode 617)
给你两棵二叉树: root1 和 root2 。
想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。
你需要将这两棵树合并成一棵新二叉树。合并的规则是:
- 如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;
- 否则,不为 null 的节点将直接作为新二叉树的节点。
返回合并后的二叉树。
注意: 合并过程必须从两个树的根节点开始。
class Solution {
// 直接在原二叉树上进行修改
// 新二叉树base在子树1上
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if(root1 == null && root2 == null)
return null;
else if(root1 != null && root2 == null)
return root1;
else if(root1 == null && root2 != null)
return root2;
else {
root1.val += root2.val;
root1.left = mergeTrees(root1.left, root2.left);
root1.right = mergeTrees(root1.right, root2.right);
return root1;
}
}
}
16. 二叉搜索树是否合法(LeetCode 98)
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
- 左子树的所有节点值 < 根节点值
- 右子树的所有节点值 > 根节点值
- 左右子树都是二叉搜索树
重要性质:二叉搜索树的中序遍历序列是递增的。
// 方法一
class Solution {
// 二叉搜索树的中序遍历序列应该是递增的
public boolean isValidBST(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
inorderTraverse(root, result);
// 检查中序序列是否是递增的
for(int i = 0; i < result.size() - 1; ++i){
if(result.get(i) >= result.get(i+1))
return false;
}
return true;
}
public void inorderTraverse(TreeNode root, List<Integer> result) {
if(root == null)
return ;
inorderTraverse(root.left, result);
result.add(root.val);
inorderTraverse(root.right, result);
}
}
//方法二
class Solution {
long maxValue = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
if(root == null)
return true;
boolean leftFlag = isValidBST(root.left);
if(root.val > maxValue)
maxValue = root.val;
else
return false;
boolean rightFlag = isValidBST(root.right);
return leftFlag && rightFlag;
}
}
17. 二叉搜索树的最小绝对差(LeetCode 530)
给你一棵二叉搜索树,请你返回 树中任意两不同节点值之间的最小绝对差值 。
思路:
- 还是利用二叉搜索树中序遍历递增的性质
- 可以通过中序遍历,将数值保存到数组中(空间复杂度为O(n))
- 也可以用pre指针维护前一个访问的节点,每次用cur.val - pre.val (cur即为中序访问的root)即可得到两个节点之间的绝对差值
class Solution {
// 二叉搜索树的中序遍历序列是递增的
// 在中序遍历的时候 min = Math.min(min, root.val - pre.val);
int min = Integer.MAX_VALUE;
TreeNode pre = null;
public int getMinimumDifference(TreeNode root) {
inorderTraverse(root);
return min;
}
public void inorderTraverse(TreeNode root) {
if(root == null)
return ;
// 左
inorderTraverse(root.left);
// 中
if(pre != null)
min = Math.min(min, root.val - pre.val);
pre = root;
// 右
inorderTraverse(root.right);
return ;
}
}
18. 二叉搜索树中的众数(LeetCode 501)
给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
如果树中有不止一个众数,可以按 任意顺序 返回。
注意:
- 如果count > maxCount是,原先存入result的元素都需要
clear()
移除 - 在处理中序节点的时候,记得更新pre
- ArrayList
转化为int[],似乎不能通过toArray直接转,需要通过一次循环
class Solution {
List<Integer> result = new ArrayList<Integer>();
TreeNode pre = null;
int maxCount = 0;
int count = 0;
public int[] findMode(TreeNode root) {
inorderTraverse(root);
int[] array = new int[result.size()];
for(int i = 0; i < result.size() ; ++i) {
array[i] = result.get(i);
}
return array;
}
public void inorderTraverse(TreeNode root) {
if(root == null)
return ;
inorderTraverse(root.left);
if(pre == null)
count = 1;
else if(pre.val != root.val)
count = 1;
else
count++;
if(count == maxCount) {
result.add(root.val);
}
// 如果出现大于maxCount的元素,则之前result中的众数都作废
if(count > maxCount) {
result.clear();
result.add(root.val);
maxCount = count;
}
// 更新pre
pre = root;
inorderTraverse(root.right);
return ;
}
}
19. 二叉树的最近公共祖先(LeetCode 236) (有难度)
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:
“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,
满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
class Solution {
// 有难度
// 自底向上,通过后序来实现回溯
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 left;
else if(left == null && right != null)
return right;
else
return null;
}
}
20. 二叉搜索树的最近公共祖先(LeetCode 235)
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:
“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,
满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
注意
- 二叉搜索树自带方向,不需要通过回溯,只需要判断
root.val
和p.val
,q.val
的大小关系即可
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root.val > p.val && root.val > q.val)
return lowestCommonAncestor(root.left, p, q);
if(root.val < p.val && root.val < q.val)
return lowestCommonAncestor(root.right, p, q);
return root;
}
}
21. 二叉搜索树中插入一个节点(LeetCode 701)
给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。
输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if(root == null)
return new TreeNode(val);
if(val < root.val)
root.left = insertIntoBST(root.left, val);
if(val > root.val)
root.right = insertIntoBST(root.right, val);
return root;
}
}
22. 二叉搜索树中删除一个节点(LeetCode 450)(有难度)
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。
返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
- 首先找到需要删除的节点;
- 如果找到了,删除它。
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
// 终止条件
if(root == null)
return null;
if(key == root.val) {
if(root.left == null && root.right == null)
return null;
else if(root.left != null && root.right == null)
return root.left;
else if(root.left == null && root.right != null)
return root.right;
// 待删除的节点左右都不为空
else {
TreeNode cur = root.right;
while(cur.left != null)
cur = cur.left;
// 把待删除节点的左子树 root.left
// 挂到待删除节点的右子树中最小的节点的左边 cur.left
cur.left = root.left;
// root.right代替root原先的位置
return root.right;
}
}
//递归逻辑
if(key < root.val)
root.left = deleteNode(root.left, key);
if(key > root.val)
root.right = deleteNode(root.right, key);
return root;
}
}
23. 修剪二叉搜索树(LeetCode 669)
给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。
通过修剪二叉搜索树,使得所有节点的值在[low, high]中。
修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。
可以证明,存在 唯一的答案 。
所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。
思路:
- 和
删除二叉搜索树中的节点
的逻辑类似 - 删除某个节点后,终结条件中用新节点作为返回值,作为被删除节点的父节点的新孩子
class Solution {
public TreeNode trimBST(TreeNode root, int low, int high) {
if(root == null)
return null;
if(root.val < low)
return trimBST(root.right, low, high);
if(root.val > high)
return trimBST(root.left, low, high);
root.left = trimBST(root.left, low, high);
root.right = trimBST(root.right, low, high);
return root;
}
}
24. 将有序数组转换为二叉搜索树(LeetCode 108)
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return sortedArrayToBST(nums, 0, nums.length-1);
}
public TreeNode sortedArrayToBST(int[] nums, int start, int end) {
if(end < start)
return null;
int rootVal = nums[(start + end) / 2];
TreeNode root = new TreeNode(rootVal);
root.left = sortedArrayToBST(nums, start, (start + end) / 2 - 1);
root.right = sortedArrayToBST(nums, (start + end) / 2 + 1, end);
return root;
}
}
25. 将二叉搜索树转化为累加树(LeetCode 538)
给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),
使得每个节点的值是原来的节点值加上所有大于它的节点值之和。
思路:
- 对于递增数组[2, 5, 13]的累加和是[20,18,13],从后往前求
- 对应二叉搜索树的访问顺序是 右-中-左
class Solution {
int sum = 0;
public TreeNode convertBST(TreeNode root) {
traverse(root);
return root;
}
public void traverse(TreeNode root) {
if(root == null)
return ;
traverse(root.right);
sum += root.val;
root.val = sum;
traverse(root.left);
return ;
}
}