目录
二叉树问题的通用解题框架:
1.验证二叉搜索树
2.二叉搜索树迭代器
3. Search in a Binary Search Tree
4. Insert into a Binary Search Tree
5.Delete Node in a BST
6.Kth Largest Element in a Stream
7.二叉搜索树的最近公共祖先
8.存在重复元素 III
9. 平衡二叉树
10.将有序数组转换为二叉搜索树
public void searchBST(TreeNode root,int key){
if(root == null){
return null;
}
if(key == root.val){
// todo 要进行的操作
}else if(key>root.val){
searchBST(root.right,key);
}else{
searchBST(root.left,key);
}
}
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
示例 1:
输入:
2
/ \
1 3
输出: true
示例 2:
输入:
5
/ \
1 4
/ \
3 6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。
解题思路:
1.依据二叉查找树中序遍历顺序一定为升序作为判断依据,上一个遍历到的节点一定小于当前遍历的节点,使用中序遍历重复上一个节点的值与当前节点比较,以判断是否为二叉查找树。
2.同样是依据中序遍历结果升序作为判断依据,直接中序遍历并暂存,直接判断暂存数据是否是升序排列的。
class Solution {
private long pre = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
if(root == null)
return true;
if(!isValidBST(root.left))
return false;
if(pre>=root.val)
return false;
pre = root.val;
return isValidBST(root.right);
}
}
实现一个二叉搜索树迭代器。你将使用二叉搜索树的根节点初始化迭代器。
调用 next()
将返回二叉搜索树中的下一个最小的数。
示例:
BSTIterator iterator = new BSTIterator(root);
iterator.next(); // 返回 3
iterator.next(); // 返回 7
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 9
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 15
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 20
iterator.hasNext(); // 返回 false
提示:
next()
和 hasNext()
操作的时间复杂度是 O(1),并使用 O(h) 内存,其中 h 是树的高度。next()
调用总是有效的,也就是说,当调用 next()
时,BST 中至少存在一个下一个最小的数。解题思路:
常规思路是将BST中序遍历并存在List中,只需要记录index,依次累加取出并移除节点数值即可。hasNext()判断List是否为空。但题干中要求空间复杂度是O(h),而储存中序遍历结果这种方式的空间复杂度是O(n),显然不太适合。
而另一种方式是根据二叉查找树的特性,将左子树的节点依次入栈,并在节点弹栈时判断当前节点是否有右子节点,如果有同样入栈。这样依次将栈内节点数据弹出,即是升序迭代。且也满足空间复杂度O(h)的要求。
class BSTIterator {
private Stack stack;
public BSTIterator(TreeNode root) {
this.stack = new Stack<>();
leftMostInorder(root);// 将左子树的左节点依次放入栈内
}
private void leftMostInorder(TreeNode root){
while(root!=null){
stack.push(root);
root = root.left;
}
}
/** @return the next smallest number */
public int next() {
TreeNode node = stack.pop();
if(node.right!=null){
leftMostInorder(node.right);
}
return node.val;
}
/** @return whether we have a next smallest number */
public boolean hasNext() {
return !stack.isEmpty();
}
}
给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。
例如,
给定二叉搜索树:
4
/ \
2 7
/ \
1 3
和值: 2
你应该返回如下子树:
2
/ \
1 3
在上述示例中,如果要找的值是 5
,但因为没有节点值为 5
,我们应该返回 NULL
。
题解思路:
二叉搜索树 查找节点,判断target值是否大于root节点值,大于继续与root右子节点比较,小于root继续与左子节点比较,直到找到与target值相等的节点。
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
if(root == null)
return null;
if(root.val==val)
return root;
if(root.val>val){
return searchBST(root.left,val);
}else{
return searchBST(root.right,val);
}
}
}
给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 保证原始二叉搜索树中不存在新值。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回任意有效的结果。
例如,
给定二叉搜索树:
4
/ \
2 7
/ \
1 3
和 插入的值: 5
你可以返回这个二叉搜索树:
4
/ \
2 7
/ \ /
1 3 5
或者这个树也是有效的:
5
/ \
2 7
/ \
1 3
\
4
题解思路:
二叉查找树插入,基本步骤与查找节点相同,同样判断target值是否大于root节点值,大于继续与root右子节点比较,小于root继续与左子节点比较,在比较时,同时判断需要比较的左节点或右节点为null时,直接插入该节点。
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if(root==null){
root= new TreeNode(val);
return root;
}
if(root.val >val){
root.left = insertIntoBST(root.left,val);
}
if(root.val
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
说明: 要求算法时间复杂度为 O(h),h 为树的高度。
示例:
root = [5,3,6,2,4,null,7]
key = 3
5
/ \
3 6
/ \ \
2 4 7
给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。
一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。
5
/ \
4 6
/ \
2 7
另一个正确答案是 [5,2,6,null,4,null,7]。
5
/ \
2 6
\ \
4 7
题解思路:
二叉查找树删除节点,首先需要查找到要删除节点,获取到该节点后根据其子节点的个数,需要考虑三种情况:
1.该节点为叶子节点,直接移除该目标节点
2.该节点只有左子树或者右子树,直接用其子节点替换该节点
3.该节点具有左右子树,我们需要用其中序的后继节点或者前驱节点来替换,再删除该目标节点
前两种情况比较好理解,主要是第三种情况,不意味着我们需要以目标节点在进行一次中序遍历,只需要找到左子树的最大值(前驱)或者右子树的最小值(后继)替换目标节点,并删除原节点即可。
public TreeNode deleteBST(TreeNode root,int key){
if(root==null){
return null;
}
if(key == root.val){
if(root.left==null&&root.right==null){
root = null;
}else if(root.right == null){// 获取右子树的最小值节点
root.val = getRightMin(root.right);
root.right = deleteBST(root.right,root.val);
}else{// 获取左子树最大值节点
root.val = getLeftMax(root.left);
root.left = deleteBST(root.left,root.val);
}
}else if(key>root.val){
root.right = deleteBST(root.right,key);
}else{
root.left = deleteBST(root.left,key);
}
}
private int getLeftMax(TreeNode root){
root = root.left;
while(root.right!=null){
root = root.right;
}
return root.val;
}
private int getRightMin(TreeNode root){
root = root.right;
while(root.left!=null){
root = root.left;
}
return root.val;
}
设计一个找到数据流中第K大元素的类(class)。注意是排序后的第K大元素,不是第K个不同的元素。
你的 KthLargest
类需要一个同时接收整数 k
和整数数组nums
的构造器,它包含数据流中的初始元素。每次调用 KthLargest.add
,返回当前数据流中第K大的元素。
示例:
int k = 3;
int[] arr = [4,5,8,2];
KthLargest kthLargest = new KthLargest(3, arr);
kthLargest.add(3); // returns 4
kthLargest.add(5); // returns 5
kthLargest.add(10); // returns 5
kthLargest.add(9); // returns 8
kthLargest.add(4); // returns 8
说明:
你可以假设 nums
的长度≥ k-1
且k
≥ 1。
class KthLargest {
final PriorityQueue queue;
final int k;
public KthLargest(int k, int[] nums) {
this.queue = new PriorityQueue(k);
this.k = k;
for(int num:nums){
add(num);
}
}
public int add(int val) {
if(queue.size()
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
示例 1:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 输出: 6 解释: 节点2
和节点8
的最近公共祖先是6。
示例 2:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4 输出: 2 解释: 节点2
和节点4
的最近公共祖先是2
, 因为根据定义最近公共祖先节点可以为节点本身。
说明:
题解思路:
1.利用BST特性,判断两个节点的值是都大于、小于还是一个大于一个小于(也可能是一个大于/小于一个等于)当前节点,当一个大于一个小于(一个大于/小于一个等于))时,说明p、q两个节点分别位于当前节点的左右子树中,则最近的公共祖先节点就是当前节点。如果都大于或者都小于时,则继续与当前节点的右子节点、左子节点比较。
2.直接使用二叉树查找最近公共祖先的方式,遍历查找并向上返回,具体参见上一篇文章《LeetCode二叉树算法题》
给定一个整数数组,判断数组中是否有两个不同的索引 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值最大为 t,并且 i 和 j 之间的差的绝对值最大为 ķ。
示例 1:
输入: nums = [1,2,3,1], k = 3, t = 0
输出: true
示例 2:
输入: nums = [1,0,1,1], k = 1, t = 2
输出: true
示例 3:
输入: nums = [1,5,9,1,5,9], k = 2, t = 3
输出: false
class Solution {
public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
TreeSet set = new TreeSet<>();
int low = 0;
for(int i = 0;ik){
set.remove(nums[low++]);
}
}
return false;
}
}
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
示例 1:
给定二叉树 [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
返回 true
。
示例 2:
给定二叉树 [1,2,2,3,3,null,null,4,4]
1
/ \
2 2
/ \
3 3
/ \
4 4
返回 false
。
题解思路:
自底至顶依靠递归一次计算每个节点的左右子树的深度,同时判断高度差是否>1,如果有其中一个节点的左右子树节点高度差大于1,说明该二叉树并不是平衡二叉树。
class Solution {
public boolean isBalanced(TreeNode root) {
return checkBalanced(root)!=-1;
}
public int checkBalanced(TreeNode root) {
if(root == null)
return 0;
int left = checkBalanced(root.left);// 分别计算左右子树的深度
if(left==-1)// 左子树的左右子树不是平衡二叉树
return -1;
int right = checkBalanced(root.right);
if(right==-1)
return -1;
return Math.abs(left-right)<2?Math.max(left,right)+1:-1;
}
}
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定有序数组: [-10,-3,0,5,9],
一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
题解思路:
利用二分法确定根节点并以此为边界,将数组分为左右两部分,分别对应左右子树的节点。使用递归方式构建二叉查找树。
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return sat(nums,0,nums.length-1);
}
private TreeNode sat(int[]nums,int start,int end){
if(start>end)
return null;
int mid = (start+end)/2;
TreeNode root = new TreeNode(nums[mid]);
root.left = sat(nums,start,mid-1);
root.right = sat(nums,mid+1,end);
return root;
}
}