樹的理解(四):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;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章