LeetCode力扣二叉查找树卡片——题解与思路

目录

二叉树问题的通用解题框架:

1.验证二叉搜索树

2.二叉搜索树迭代器

3. Search in a Binary Search Tree

4. Insert into a Binary Search Tree

5.Delete Node in a BST

6.Kth Largest Element in a Stream

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

8.存在重复元素 III

9. 平衡二叉树

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


 

二叉树问题的通用解题框架:

public void searchBST(TreeNode root,int key){
    if(root == null){
       return null; 
    }
    if(key == root.val){
    // todo 要进行的操作
    }else if(key>root.val){
        searchBST(root.right,key);
    }else{
        searchBST(root.left,key); 
    }
}   

1.验证二叉搜索树

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

  • 节点的左子树只包含小于当前节点的数。
  • 节点的右子树只包含大于当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

示例 1:

输入:
    2
   / \
  1   3
输出: true

示例 2:

输入:
    5
   / \
  1   4
     / \
    3   6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
     根节点的值为 5 ,但是其右子节点值为 4 。

解题思路:

1.依据二叉查找树中序遍历顺序一定为升序作为判断依据,上一个遍历到的节点一定小于当前遍历的节点,使用中序遍历重复上一个节点的值与当前节点比较,以判断是否为二叉查找树。

2.同样是依据中序遍历结果升序作为判断依据,直接中序遍历并暂存,直接判断暂存数据是否是升序排列的。

class Solution {
    
    private long pre = Long.MIN_VALUE;
    
    public boolean isValidBST(TreeNode root) {
        if(root == null)
            return true;
        if(!isValidBST(root.left))
            return false;
        if(pre>=root.val)
            return false;
        pre = root.val;
        return isValidBST(root.right);
    }
}

2.二叉搜索树迭代器

实现一个二叉搜索树迭代器。你将使用二叉搜索树的根节点初始化迭代器。

调用 next() 将返回二叉搜索树中的下一个最小的数。

 

示例:

BSTIterator iterator = new BSTIterator(root);
iterator.next();    // 返回 3
iterator.next();    // 返回 7
iterator.hasNext(); // 返回 true
iterator.next();    // 返回 9
iterator.hasNext(); // 返回 true
iterator.next();    // 返回 15
iterator.hasNext(); // 返回 true
iterator.next();    // 返回 20
iterator.hasNext(); // 返回 false

 

提示:

  • next() 和 hasNext() 操作的时间复杂度是 O(1),并使用 O(h) 内存,其中 是树的高度。
  • 你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 中至少存在一个下一个最小的数。

解题思路:

常规思路是将BST中序遍历并存在List中,只需要记录index,依次累加取出并移除节点数值即可。hasNext()判断List是否为空。但题干中要求空间复杂度是O(h),而储存中序遍历结果这种方式的空间复杂度是O(n),显然不太适合。

而另一种方式是根据二叉查找树的特性,将左子树的节点依次入栈,并在节点弹栈时判断当前节点是否有右子节点,如果有同样入栈。这样依次将栈内节点数据弹出,即是升序迭代。且也满足空间复杂度O(h)的要求。

class BSTIterator {

    private Stack stack;
    
    public BSTIterator(TreeNode root) {
        this.stack = new Stack<>();
        
        leftMostInorder(root);// 将左子树的左节点依次放入栈内
    }
    
    private void leftMostInorder(TreeNode root){
        while(root!=null){
            stack.push(root);
            root = root.left;
        }
    }
    
    /** @return the next smallest number */
    public int next() {
        TreeNode node = stack.pop();
        if(node.right!=null){
            leftMostInorder(node.right);
        }
        return node.val;
    }
    
    /** @return whether we have a next smallest number */
    public boolean hasNext() {
        return !stack.isEmpty();
    }
}

3. Search in a Binary Search Tree

 

给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。

例如,

给定二叉搜索树:

        4
       / \
      2   7
     / \
    1   3

和值: 2

你应该返回如下子树:

      2     
     / \   
    1   3

在上述示例中,如果要找的值是 5,但因为没有节点值为 5,我们应该返回 NULL

题解思路:

二叉搜索树 查找节点,判断target值是否大于root节点值,大于继续与root右子节点比较,小于root继续与左子节点比较,直到找到与target值相等的节点。

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);
        }else{
            return searchBST(root.right,val);
        }
    }
}

 

4. Insert into a Binary Search Tree

 

给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 保证原始二叉搜索树中不存在新值。

注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回任意有效的结果。

例如, 

给定二叉搜索树:

        4
       / \
      2   7
     / \
    1   3

和 插入的值: 5

你可以返回这个二叉搜索树:

         4
       /   \
      2     7
     / \   /
    1   3 5

或者这个树也是有效的:

         5
       /   \
      2     7
     / \   
    1   3
         \
          4

题解思路:

二叉查找树插入,基本步骤与查找节点相同,同样判断target值是否大于root节点值,大于继续与root右子节点比较,小于root继续与左子节点比较,在比较时,同时判断需要比较的左节点或右节点为null时,直接插入该节点。

 

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

5.Delete Node in a BST

 

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

  1. 首先找到需要删除的节点;
  2. 如果找到了,删除它。

说明: 要求算法时间复杂度为 O(h),h 为树的高度。

示例:

root = [5,3,6,2,4,null,7]
key = 3

    5
   / \
  3   6
 / \   \
2   4   7

给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。

一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。

    5
   / \
  4   6
 /     \
2       7

另一个正确答案是 [5,2,6,null,4,null,7]。

    5
   / \
  2   6
   \   \
    4   7

题解思路:

二叉查找树删除节点,首先需要查找到要删除节点,获取到该节点后根据其子节点的个数,需要考虑三种情况:

1.该节点为叶子节点,直接移除该目标节点

2.该节点只有左子树或者右子树,直接用其子节点替换该节点

3.该节点具有左右子树,我们需要用其中序的后继节点或者前驱节点来替换,再删除该目标节点

前两种情况比较好理解,主要是第三种情况,不意味着我们需要以目标节点在进行一次中序遍历,只需要找到左子树的最大值(前驱)或者右子树的最小值(后继)替换目标节点,并删除原节点即可。

public TreeNode deleteBST(TreeNode root,int key){
    if(root==null){
        return null;
    }
    if(key == root.val){
        if(root.left==null&&root.right==null){
            root = null;
         }else if(root.right == null){// 获取右子树的最小值节点
            root.val = getRightMin(root.right);
            root.right = deleteBST(root.right,root.val);
        }else{// 获取左子树最大值节点
            root.val = getLeftMax(root.left);
            root.left = deleteBST(root.left,root.val);
        }
    }else if(key>root.val){
         root.right = deleteBST(root.right,key);
    }else{
        root.left = deleteBST(root.left,key);
    }
}

private int getLeftMax(TreeNode root){
    root = root.left;
    while(root.right!=null){
        root = root.right;
    }
    return root.val;
}

private int getRightMin(TreeNode root){
    root = root.right;
    while(root.left!=null){
        root = root.left;
    }
    return root.val;
}

6.Kth Largest Element in a Stream

 

 

设计一个找到数据流中第K大元素的类(class)。注意是排序后的第K大元素,不是第K个不同的元素。

你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含数据流中的初始元素。每次调用 KthLargest.add,返回当前数据流中第K大的元素。

示例:

int k = 3;
int[] arr = [4,5,8,2];
KthLargest kthLargest = new KthLargest(3, arr);
kthLargest.add(3);   // returns 4
kthLargest.add(5);   // returns 5
kthLargest.add(10);  // returns 5
kthLargest.add(9);   // returns 8
kthLargest.add(4);   // returns 8

说明:
你可以假设 nums 的长度≥ k-1 且k ≥ 1。

class KthLargest {
    
    final PriorityQueue queue;
    final int k;

    public KthLargest(int k, int[] nums) {
        this.queue = new PriorityQueue(k);
        this.k = k;
        for(int num:nums){
            add(num);
        }
    }
    
    public int add(int val) {
        if(queue.size()

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

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉搜索树:  root = [6,2,8,0,4,7,9,null,null,3,5]

 

示例 1:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6 
解释: 节点 2 和节点 8 的最近公共祖先是 6。

示例 2:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。

 

说明:

  • 所有节点的值都是唯一的。
  • p、q 为不同节点且均存在于给定的二叉搜索树中。

题解思路:

1.利用BST特性,判断两个节点的值是都大于、小于还是一个大于一个小于(也可能是一个大于/小于一个等于)当前节点,当一个大于一个小于(一个大于/小于一个等于))时,说明p、q两个节点分别位于当前节点的左右子树中,则最近的公共祖先节点就是当前节点。如果都大于或者都小于时,则继续与当前节点的右子节点、左子节点比较。

2.直接使用二叉树查找最近公共祖先的方式,遍历查找并向上返回,具体参见上一篇文章《LeetCode二叉树算法题》

 

8.存在重复元素 III

 

给定一个整数数组,判断数组中是否有两个不同的索引 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值最大为 t,并且 i 和 j 之间的差的绝对值最大为 ķ

示例 1:

输入: nums = [1,2,3,1], k = 3, t = 0
输出: true

示例 2:

输入: nums = [1,0,1,1], k = 1, t = 2
输出: true

示例 3:

输入: nums = [1,5,9,1,5,9], k = 2, t = 3
输出: false
class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        TreeSet set = new TreeSet<>();
        int low = 0;
        for(int i = 0;ik){
                set.remove(nums[low++]);
            }
        }
        return false;
    }
}

9. 平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

示例 1:

给定二叉树 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7

返回 true 。

示例 2:

给定二叉树 [1,2,2,3,3,null,null,4,4]

       1
      / \
     2   2
    / \
   3   3
  / \
 4   4

返回 false 。

题解思路:

自底至顶依靠递归一次计算每个节点的左右子树的深度,同时判断高度差是否>1,如果有其中一个节点的左右子树节点高度差大于1,说明该二叉树并不是平衡二叉树。

class Solution {
    public boolean isBalanced(TreeNode root) {
        return checkBalanced(root)!=-1;
    }
    
     public int checkBalanced(TreeNode root) {
        if(root == null)
            return 0;
        int left = checkBalanced(root.left);// 分别计算左右子树的深度
        if(left==-1)// 左子树的左右子树不是平衡二叉树
            return -1;
        int right = checkBalanced(root.right);
        if(right==-1)
            return -1;
        return Math.abs(left-right)<2?Math.max(left,right)+1:-1;
    }
}      

 

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

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

示例:

给定有序数组: [-10,-3,0,5,9],

一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:

      0
     / \
   -3   9
   /   /
 -10  5

题解思路:

利用二分法确定根节点并以此为边界,将数组分为左右两部分,分别对应左右子树的节点。使用递归方式构建二叉查找树。

 

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return sat(nums,0,nums.length-1);
    }
    
    private TreeNode sat(int[]nums,int start,int end){
        if(start>end)
            return null;
        int mid = (start+end)/2;
        TreeNode root = new TreeNode(nums[mid]);
        root.left = sat(nums,start,mid-1);
        root.right = sat(nums,mid+1,end);
        return root;
    }
}

 

你可能感兴趣的:(LeetCode算法题)