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