LeetCode---BST

669.修剪二叉搜索树

题目描述

给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树不应该改变保留在树中的元素的相对结构(即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在唯一的答案。
所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。
在这里插入图片描述

解题思路

如果根节点的值比给定的右边界还大,由二叉搜索树的性质可知,右子树的所有值也都是比给定的右边界大的,那么就在左子树中进行修剪即可。
同理,如果根节点的值比给定的左边界还小,左子树的所有值也都是比给定的左边界小的值,那么只用在右子树中进行修剪即可。
如果根节点的值在给定的左边界和右边界之间,那么就采用递归分别在左右子树中进行修剪作为左右子树。

代码实现

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);
        }
        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;
    }
}

230.二叉搜索树的第k小元素

题目大意

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。
在这里插入图片描述

解题思路

(一)递归实现:

  1. 二叉搜索树的中序遍历是有序的,第k小元素就是中序遍历的k-1个元素。
    时间复杂度:O(N),遍历整棵树
    空间复杂度:O(N),利用数组来存储中序遍历序列。
  2. 不将中序遍历的结果保存到集合中了,而是根据计数,到达访问中序遍历的第k个元素的时候,就将其返回。
  3. 计算左子树中节点的个数,如果左子树中节点的个数为k-1个,那么整棵树的根节点就是第k小的元素;如果左子树的节点个数大于k-1那么第k小元素就在左子树中递归来找;反之,如果小于k-1,那么就去右子树中去找。
    (二)迭代实现
    在栈的帮助下,可以将方法一的递归转化为迭代,这样可以加快速度,不用遍历整棵树了,只要找到第k小值就结束遍历。
    一直向左寻找左节点加入到栈中,每将一个值退出栈就将k–,当k减到0时就表示找到了第k小的元素。
    栈的创建:
LinkedList<TreeNode> stack=new LinkedList<>();

代码实现

(一)递归解法1.0

class Solution {
    public int kthSmallest(TreeNode root, int k) {
        //二叉搜索树的中序遍历是有序的,第k小元素就是中序遍历的第k-1个元素
        List<Integer> res=new ArrayList<>();
        if(root==null){
            return 0;
        }
        inorder(root,res);
        return res.get(k-1);
    }
    public void inorder(TreeNode root,List<Integer> res){
        if(root==null){
            return ;
        }
        inorder(root.left,res);
        res.add(root.val);
        inorder(root.right,res);
    }
}

递归解法2.0:去除了存放结果的集合,而是不保存中序遍历的结果直接进行返回第k小值。

private int cnt = 0;
private int val;

public int kthSmallest(TreeNode root, int k) {
    inOrder(root, k);
    return val;
}

private void inOrder(TreeNode node, int k) {
    if (node == null) return;
    inOrder(node.left, k);
    cnt++;
    if (cnt == k) {
        val = node.val;
        return;
    }
    inOrder(node.right, k);
}

递归解法3.0

class Solution {
   public int kthSmallest(TreeNode root, int k) {
    int leftCnt = count(root.left);
    //如果左子树中的节点个数等于k-1,那么根节点就是第k小的节点
    if (leftCnt == k - 1) return root.val;
    //如果个数大于k-1,那么递归在左子树中找第k小值
    if (leftCnt > k - 1) return kthSmallest(root.left, k);
    //反之,就在右子树中找,注意个数要减去左子树的节点数
    return kthSmallest(root.right, k - leftCnt - 1);
    }
    //计算以node为根的树中一共有多少个节点 利用递归来实现
    private int count(TreeNode node) {
        if (node == null) return 0;
        return 1 + count(node.left) + count(node.right);
        }
}

(二)迭代实现

class Solution {
    public int kthSmallest(TreeNode root, int k) {
        //二叉搜索树的中序遍历是有序的,第k小元素就是中序遍历的第k-1个元素
        //利用迭代来实现
        LinkedList<TreeNode> stack=new LinkedList<>();
        while(true){
            while(root!=null){
                stack.add(root);
                root=root.left;
            }
            TreeNode node=stack.removeLast();
            if(--k==0){
                return node.val;
            }
            root=node.right;
        }
    }
}

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

题目大意

给出二叉搜索树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。
提醒一下,二叉搜索树满足下列约束条件:
节点的左子树仅包含键 小于 节点键的节点。
节点的右子树仅包含键 大于 节点键的节点。
左右子树也必须是二叉搜索树。
在这里插入图片描述

解题思路

==二叉树的反向中序遍历。==二叉树搜索树的中序遍历结果是从小到大,中序遍历的反向就是从大到小。大于等于节点的值存在于根节点本身以及右子树中。
**注意代码中定义的变量sum是一路累加的。

代码实现

class Solution {
    //sum一路累加
    public int sum=0;
    public TreeNode convertBST(TreeNode root) {
        //中序遍历的反遍历
        if(root!=null){
            convertBST(root.right);
            sum+=root.val;
            root.val=sum;
            convertBST(root.left);
        }
        return root;
    }
}

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

题目大意

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树:  root = [6,2,8,0,4,7,9,null,null,3,5]

解题思路

对于二叉搜索树而言,使用递归来解决:
如果根节点的值是大于两个节点的值,那么就递归的在左子树中找两个节点的最近公共祖先;如果根节点的值小于两个节点的值,那么就在右子树中找;如果根节点的值的大小在两个节点之间,那么根节点就是两个节点的最近公共祖先。

代码实现

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

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

题目大意

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
在这里插入图片描述

解题思路

(一)递归来实现
如果其中一个节点为根节点,那么根节点就是最近公共祖先。如果不是,就递归的在左右子树中寻找。如果在左右子树中找到就返回不为空的那个,如果左右子树中都没有找到,就表示两个节点分别在左右子树中,就返回根节点。
(二)存储父节点
可以使用哈希表存储所有节点的父节点,然后我们就可以利用节点的父节点信息从p节点开始不断往上跳,并记录已经访问过的节点,再从q节点不断向上跳,如果碰到已经访问过的节点,那么这个节点就是我们要找的最近公共祖先。
算法:

  1. 从根节点开始遍历整棵二叉树,用哈希表记录每个节点的父节点指针。
  2. 从p节点开始不断往他的祖先移动,并用数据结构记录已经访问过的祖先节点。
  3. 同样,我们再从q节点开始不断往他的祖先的移动,如果有祖先已经被访问过,即意味着这是p和q的深度最深的公共祖先,即LCA节点。

代码实现

(一)递归来实现

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);
        return left==null?right:right==null?left:root;
    }
}

(二)存储父节点

class Solution {
     //利用HashMap来存储当前节点的值以及父节点
    HashMap<Integer,TreeNode> parent=new HashMap<>();
    //利用HashSet来存储已经访问过的节点 存放无序的不可重复的元素
    Set<Integer> visited=new HashSet<>();
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //递归来实现遍历整棵树 将节点值以及父节点放入到哈希表中
        dfs(root);
        while(p!=null){
            //一直找p的父节点,并标记已经访问过
            visited.add(p.val);
            p=parent.get(p.val);
        }
        while(q!=null){
            //找q的父节点,并判断是否被访问过,如果被访问过则该点就是最近的公共祖先
            if(visited.contains(q.val)){
                return q;
            }
            q=parent.get(q.val);
            
        }
        return null;
        
    }
    public void dfs(TreeNode root){
       
        if(root.left!=null){
            parent.put(root.left.val,root);
            dfs(root.left);
        }
        if(root.right!=null){
            parent.put(root.right.val,root);
            dfs(root.right);
        }

    }
}

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

题目大意

给你一个整数数组 nums ,其中元素已经按升序排列,请你将其转换为一棵高度平衡二叉搜索树。在这里插入图片描述
在这里插入图片描述

高度平衡二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。

解题思路

找出中间节点,作为二叉搜索树的根,然后递归的建立左右子树。
中间节点的确定方式:

int m=l+(r-l+1)/2;

代码实现

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        TreeNode root=nums==null?null:buildTree(nums,0,nums.length-1);
        return root;
    }
    public TreeNode buildTree(int[] nums,int l,int r){
        if(l>r) return null;
        int m=l+(r-l+1)/2;
        TreeNode root=new TreeNode(nums[m]);
        root.left=buildTree(nums,l,m-1);
        root.right=buildTree(nums,m+1,r);
        return root;
    }
}

109.有序链表转换为二叉搜索树

题目大意

给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
在这里插入图片描述

解题思路

(一)分治法:
设当前链表的左端点为left,右端点为right,包含关系为<左闭右开>,也就是left包含在链表中,而right不包含在链表中。为什么这样设置?
由于题目中给定的链表为单向链表,访问后继元素十分容易,但是无法直接访问前驱元素,因此在找到链表的中位数节点mid之后,如果设定左闭右开的关系,就直接可以用(left,mid)以及(mid.next,right)来表示左右子树对应的列表,而且,初始的列表也可以用(head,null)方便的进行表示,其中null表示空节点。
时间复杂度:O(nlogn),其中n是链表的长度。设长度为n的链表构建二叉搜索树的时间为T(n),递推式T(n)=2T(n/2)+O(n),根据主定理,T(n)=O(nlogn)。
空间复杂度:O(logn),这里只计算除了返回答案之外的空间。平衡二叉树的高度为O(logn),即为递归过程中栈的最大深度,也就是需要的空间。
(二)中序遍历+分治:
其实我们已经知道了这棵二叉树的结构了,我们只需要根据给定的中序遍历结果,对其进行中序遍历,就可以还原这棵二叉搜索树了。
时间复杂度:O(n),n是链表的长度。长度为n的链表构造二叉搜索树的时间为T(n),递推式T(n)=2
T(n/2)+O(1),根据主定理,T(n)=O(n)。
空间复杂度:O(logn),平衡二叉树的高度为O(logn),即为递归过程中栈的最大深度,也就是需要的空间。
(三)将有序链表转化为有序数组
同上一题的实现过程,只是多了将链表转化为数组这一过程。

代码实现

(一)分治法:

class Solution {
    //快慢指针&递归建树
    public TreeNode sortedListToBST(ListNode head) {
        TreeNode root;
        if(head==null){
            return null;
        }
        if(head.next==null){
            return root=new TreeNode(head.val);
        }
        return root=buildTree(head,null);
    }
    public TreeNode buildTree(ListNode left,ListNode right){
        if(left==right){
            return null;
        }
        ListNode mid=find(left,right);
        TreeNode root=new TreeNode(mid.val);
        root.left=buildTree(left,mid);
        root.right=buildTree(mid.next,right);
        return root;
    }
    //利用快慢指针找链表的中间节点
    public ListNode find(ListNode l,ListNode r){
        ListNode fast=l;
        ListNode slow=l;
        while(fast!=r&&fast.next!=r){
            fast=fast.next.next;
            slow=slow.next;
        }
        return slow;
    }
}

(二)中序遍历+分治

class Solution {
    //分治+中序遍历
    ListNode globalhead; 
    public TreeNode sortedListToBST(ListNode head) {
        if(head==null){
            return null;
        }
        if(head.next==null){
            return new TreeNode(head.val);
        }
        globalhead=head;
        int length=countLength(head);
        TreeNode root=buildTree(0,length-1);
        return root;  
    }
    public int countLength(ListNode head){
        int count=0;
        ListNode p=head;
        while(p!=null){
            count++;
            p=p.next;
        }
        return count;
    }
    public TreeNode buildTree(int l,int r){
        if(l>r){
            return null;
        }
        int mid=(l+r+1)/2;
        //制造一个空节点
        TreeNode root=new TreeNode();
        root.left=buildTree(l,mid-1);
        root.val=globalhead.val;
        globalhead=globalhead.next;
        root.right=buildTree(mid+1,r);
        return root;
    }
}

653.两数之和IV–输入BST

题目大意

给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。
在这里插入图片描述

解题思路

(一)中序遍历+双指针
中序遍历整棵树将遍历结果保存到一个数组中,然后使用双指针来看有没有两个值的和为给定的target
(二)使用HashSet:
当遍历到一个点时,查看set集合中是否存在k-root.val的值,如果存在,则返回true,否则就将节点的值加入到集合中,然后递归的在左右子树中查找。
(三)BFS+HashSet
方法二使用递归,而BFS使用迭代来遍历整棵树,在对树进行广搜的时候进行保存和查找。
注意队列的创建是用LinkedList结构:

Queue<TreeNode> queue=new LinkedList<>();

代码实现

class Solution {
    public boolean findTarget(TreeNode root, int k) {
        //中序遍历为有序数组+双指针
        List<Integer> res=new ArrayList<>();
        if(root==null){
            return false;
        }
        inorder(root,res);
        //res是将二叉搜索树升序排列的集合
        int i=0,j=res.size()-1;
        while(i<j){
            if(res.get(i)+res.get(j)==k){
                return true;
            }
            if(res.get(i)+res.get(j)<k){
                i++;
            }
            if(res.get(i)+res.get(j)>k){
                j--;
            }
        }
        return false;
        
    }
    public void inorder(TreeNode root,List<Integer> res){
        if(root==null){
            return ;
        }
        inorder(root.left,res);
        res.add(root.val);
        inorder(root.right,res);
    } 
}

(二)HashSet来解决

class Solution {
    public boolean findTarget(TreeNode root, int k) {
        //使用HashSet:存放无序不可重复的元素
        if(root==null){
            return false;
        }
        Set<Integer> set=new HashSet<>();
        return find(root,k,set);
    }
    public boolean find(TreeNode root,int k,Set<Integer> set){
        if(root==null){
            return false;
        }
        if(set.contains(k-root.val)){
            return true;
        }
        set.add(root.val);
        return find(root.left,k,set)||find(root.right,k,set);
    }
}

(三)BFS+HashSet

class Solution {
    public boolean findTarget(TreeNode root, int k) {
        //使用HashSet:存放无序不可重复的元素
        if(root==null){
            return false;
        }
        Set<Integer> set=new HashSet<>();
        Queue<TreeNode> queue=new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
            if(queue.peek()!=null){
                TreeNode node=queue.remove();
                if(set.contains(k-node.val)){
                    return true;
                }
                set.add(node.val);
                queue.add(node.left);
                queue.add(node.right);
            }else{
                queue.remove();
            }
        }
        return false;
    }
}

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

题目大意

给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。
在这里插入图片描述

解题思路

(一)中序遍历找最小差值
对二叉搜索树进行中序遍历得到从小到大的序列,然后将相邻的元素进行比较查找差最小的两个元素。
(二)是对方法一的优化,不使用list保存中序遍历的结果了,而是在遍历的同时查找最小值,要对中序遍历的前一个节点进行保存。

代码实现

(一)中序遍历找最小差值

class Solution {
    public int getMinimumDifference(TreeNode root) {
        List<Integer> list=new ArrayList<>();
        if(root==null){
            return 0;
        }
        inorder(root,list);
        int res=list.get(1)-list.get(0);
        for(int i=2;i<list.size();i++){
            res=Math.min(res,list.get(i)-list.get(i-1));
        }
        return res;
    }
    public void inorder(TreeNode root,List<Integer> list){
        if(root==null){
            return ;
        }
        inorder(root.left,list);
        list.add(root.val);
        inorder(root.right,list);
    }
}

(二)不保存中序遍历结果

class Solution {
    private int minValue=Integer.MAX_VALUE;
    private TreeNode preNode=null;
    public int getMinimumDifference(TreeNode root) {
        List<Integer> list=new ArrayList<>();
        if(root==null){
            return 0;
        }
        inorder(root);
        return minValue;
    }
    public void inorder(TreeNode root){
        if(root==null){
            return ;
        }
        inorder(root.left);
        if(preNode!=null){
            minValue=Math.min(minValue,root.val-preNode.val);
        }
        preNode=root;
        inorder(root.right);
    }
}

501.二叉搜索树中的众数

题目大意

给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。
假定 BST 有如下定义:
结点左子树中所含结点的值小于等于当前结点的值
结点右子树中所含结点的值大于等于当前结点的值
左子树和右子树都是二叉搜索树。
在这里插入图片描述

解题思路

对二叉搜索树进行中序遍历,在中序遍历的时候对重复节点值进行计数,也要保存节点的前一个值,因为会出现很多值出现的次数相等,所以用一个集合来保存,最后还要将集合中的元素保存到一个数组中进行输出。

代码实现

class Solution {
    private int maxCnt=1;
    private int cnt=1;
    private TreeNode preNode=null;
    public int[] findMode(TreeNode root) {
        List<Integer> list=new ArrayList<>();
        //中序
        inorder(root,list);
        int[] res=new int[list.size()];
        int idx=0;
        for(int value:list){
            res[idx++]=value;
        }
        return res;      
    }
    public void inorder(TreeNode root,List<Integer> list){
        if(root==null){
            return ;
        }
        inorder(root.left,list);
        if(preNode!=null){
            if(preNode.val==root.val){
                cnt++;
            }else{
                cnt=1;
            }
        }
        if(cnt>maxCnt){
            maxCnt=cnt;
            list.clear();
            list.add(root.val);
        }else if(cnt==maxCnt){
            list.add(root.val);

        }
        preNode=root;
        inorder(root.right,list);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值