修建二叉搜索樹
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;
}
}