树的理解(四):BST

修建二叉搜索树

https://leetcode-cn.com/problems/trim-a-binary-search-tree/description/

二叉搜索树特性就是对于一个节点来说,它的左子树的节点值都比它小,右子树的节点值都比它大。

根据题意得,如果每个节点值如果小于了L或者大于R就修剪掉,包括根节点。
所以,对于任意一个节点来说,如果root.val > R,那么就去该节点的左子树中找(左子树的节点值都比它小);如果root.val < L,那么就去它的右子树中找(右子树的节点值都比它大),进而判断它的左子和右子。

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

二叉搜索树中第K小的元素

https://leetcode-cn.com/problems/kth-smallest-element-in-a-bst/

利用了二分查找的思想,首先求出左子树的节点数leftCnt,如果该值大于了k,说明第k小的元素一定在左子树中(因为左子树小于根节点);反之一定在右子树中。如果正好等于了k,那么说明该节点就是第k小的元素。(与k-1比较是因为判断当前节点)

class Solution {
    public int kthSmallest(TreeNode root, int k) {
    int leftCnt = count(root.left);
    if (leftCnt == k - 1) return root.val;
    if (leftCnt > k - 1) return kthSmallest(root.left, k);
    return kthSmallest(root.right, k - leftCnt - 1);
}
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) {
        List<Integer> list = new ArrayList<>();
        inOrder(root,list);
        int i = 0;
        while(k-- > 1) i++;
        return list.get(i);
    }
    private void inOrder(TreeNode root,List<Integer> list) {
        if(root == null) return;
        inOrder(root.left,list);
        list.add(root.val);
        inOrder(root.right,list);
    }
}

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

https://leetcode-cn.com/problems/convert-bst-to-greater-tree/

本题考的是BST的特性,“所有大于本节点的值”可以理解为该节点右侧的所有节点的值(包括右子树以及父节点往上的所有右子树的值,如果转化为中序遍历就很好理解了,就是中序遍历结果中所有在该元素右侧的值)。
由于子节点只能找到右子树部分的累加值,无法找到父节点以上的右子树的累加值,所以定义一个全局变量保存该值,那么就先从根节点入手,先遍历右子树,并将累加值结果保存起来,以供左子树累加时使用。

class Solution {
    public TreeNode convertBST(TreeNode root) {
        int[] sum = {0};
        buildGT(root,sum);
        return root;
    }
    private void buildGT(TreeNode node,int[] sum) {
        if(node == null) return;
        buildGT(node.right,sum);
        sum[0] += node.val;
        node.val = sum[0];
        buildGT(node.left,sum);
    }
}

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

https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/

考察二叉搜索树的特性,两个节点的最近公共祖先一定位于两棵子树之间,也就是说,当两个节点都大于根节点时,那么这两个子树位于根节点的右子树;相反位于两个子树的左子树。

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

二叉树的最近公共祖先

https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/

严格来讲,这道题不应该放在这里。而是放在递归那篇文章里,但是和上一道题相似难度增高,就给它放这里了。

思路就是两个节点一定位于公共祖先的两侧。
那么就递归寻找每一个节点的左右子树,如果两个节点分别位于该节点的左、右子树中,那么这个节点就是最低公共祖先。

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

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

https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/

可以等价于中序遍历结果转化为二叉搜索树。
思路是,数组的中间值就是树的根节点,中间值左侧就为二叉搜索树的左子树,右侧为右子树,然后向下递归就可以。

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

有序链表转换二叉搜索树

https://leetcode-cn.com/problems/convert-sorted-list-to-binary-search-tree/

整体思路和上道题是一样的,也是找中间点,但是困难的是链表无法随机访问。
那么只能是每次遍历找到链表的中值,具体做法是用快慢指针,快指针一次走两步,慢指针一次走1步以此求出链表的中间值。
还要注意一些细节,就是当构建当前节点的左子树的时候,要记得将链表截断,不截断会使下次遍历还是从头到尾将原始链表遍历一遍,这样找到的中间值依然是当前节点。

class Solution {
    public TreeNode sortedListToBST(ListNode head) {
        if(head == null)return null;
        if(head.next == null) return new TreeNode(head.val);
        ListNode pre = findMid(head);
        ListNode mid = pre.next;//这个才是链表的中间值
        pre.next = null;	//将当前节点与链表后面的节点阻断
        TreeNode root = new TreeNode(mid.val);
        root.left = sortedListToBST(head);
        root.right = sortedListToBST(mid.next);
        return root;
    }
    private ListNode findMid(ListNode head) {
        if(head == null) return null;
        ListNode slow = head;
        ListNode fast = head.next;
        ListNode pre = head;	//链表无法根据当前节点找到前一个节点,所以用一个新的节点指向找到中间节点的前一个节点
        while(fast != null && fast.next != null) {
            pre = slow;
            slow = slow.next;
            fast = fast.next.next;
        }
        return pre;
    }
}

二叉搜索树和中序遍历有着密切的联系,如果拿到一道题没有思路,可以先尝试思考树的中序遍历。

两数之和 IV - 输入 BST

https://leetcode-cn.com/problems/two-sum-iv-input-is-a-bst/

class Solution {
    public boolean findTarget(TreeNode root, int k) {
        if(root == null) return false;
        List<Integer> list = new ArrayList<>();
        Stack<TreeNode> s = new Stack<>();
        while(!s.isEmpty() || root != null) {
            while(root != null) {
                s.push(root);
                root = root.left;
            }
            root = s.pop();
            list.add(root.val);
            root = root.right;
        }
        int l = 0,h = list.size() - 1;
        while(l < h) {
            if(list.get(l) + list.get(h) == k) return true;
            else if(list.get(l) + list.get(h) > k) h--;
            else l++;
        }
        return false;
    }
}

二叉搜索树的最小绝对差

https://leetcode-cn.com/problems/minimum-absolute-difference-in-bst/

class Solution {
    public int getMinimumDifference(TreeNode root) {
        if(root == null) return 0;
        int min = Integer.MAX_VALUE;
        List<Integer> list = new ArrayList<>();
        Stack<TreeNode> s = new Stack<>();
        while(!s.isEmpty() || root != null) {
            while(root != null) {
                s.push(root);
                root = root.left;
            }
            root = s.pop();
            int cur_size;
            if((cur_size = list.size()) > 0)
                min = Math.min(min,root.val - list.get(cur_size - 1));
            list.add(root.val);
            root = root.right;
        }
        return min;
    }
}

二叉搜索树中的众数

https://leetcode-cn.com/problems/find-mode-in-binary-search-tree/

class Solution {
    public int[] findMode(TreeNode root) {
        if(root == null) return new int[]{};
        Stack<TreeNode> s = new Stack<>();
        Map<Integer,Integer> map = new HashMap<>();
        int max = 0;
        while(!s.isEmpty() || root != null) {
            while(root != null) {
                s.push(root);
                root = root.left;
            }
            root = s.pop();
            if(map.containsKey(root.val)) {
                map.put(root.val,map.get(root.val) + 1);
            }else map.put(root.val,1);
            max = Math.max(max,map.get(root.val));
            root = root.right;
        }
        int count = 0;
        for(Integer i : map.keySet()) {
            if(map.get(i) == max) count ++;
        }
        int[] res = new int[count];
        int index = 0;
        for(Integer i : map.keySet()) {
            if(map.get(i) == max) res[index++] = i;
        }
        return res;
    }
}
发布了352 篇原创文章 · 获赞 73 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43777983/article/details/105135431
BST