二叉搜索树
一、二叉搜索树的属性
遇到二叉搜索树,立刻想到这句话:「二叉搜索树(BST)的中序遍历是有序的」。这是解决所有二叉搜索树问题的关键。
98-验证二叉搜索树
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:
2
/ \
1 3
输出: true
示例 2:
输入:
5
/ \
1 4
/ \
3 6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。
方法一:递归方法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool isValidBST(TreeNode* root) {
bool result = dfs(root, LONG_MIN, LONG_MAX);
return result;
}
bool dfs(TreeNode* node, long lower, long upper){
if(!node){
return true;
}
if(!(lower < node->val && node->val < upper)){
return false;
}
return dfs(node->left, lower, node->val) && dfs(node->right, node->val, upper);
}
};
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
result = self.dfs(root, float('-inf'), float('inf')) # 对于根节点,它的上下限为无穷大和无穷小
return result
def dfs(self, node, lower, upper):
if not node: # 边界条件,如果node为空肯定是二叉搜索树
return True
if not lower < node.val < upper: # 如果当前节点超出上下界范围,肯定不是
return False
# 走到下面这步说明已经满足了如题所述的二叉搜索树的前两个条件
# 那么只需要递归判断当前节点的左右子树是否同时是二叉搜索树即可
return self.dfs(node.left, lower, node.val) and self.dfs(node.right, node.val, upper)
方法二:中序递归遍历(判断节点的有序性)
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
prev = None
def dfs(node):
nonlocal prev
if not node:
return True
left_is = dfs(node.left)
if prev and prev.val >= node.val:
return False
prev = node
right_is = dfs(node.right)
return left_is and right_is
return dfs(root)
方法三:中序递归遍历(转为有序数组)
- 思路简单:将中序遍历的值一个个存入一个list中。理解二叉搜索树后,明白二叉搜索树中序遍历后是个递增序列。最后判断list是否为一个递增序列即可。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
inorder = []
# 中序遍历
def bfs(node):
if node is None:
return
if node.left is not None:
bfs(node.left)
inorder.append(node.val)
if node.right is not None:
bfs(node.right)
bfs(root)
print("inorder = ", inorder)
for i in range(1, len(inorder)):
if inorder[i] <= inorder[i - 1]:
return False
return True
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
inorder = []
# 中序遍历
def bfs(node):
if node is None:
return
if node.left is not None:
bfs(node.left)
inorder.append(node.val)
if node.right is not None:
bfs(node.right)
bfs(root)
print("inorder = ", inorder)
result = inorder == sorted(inorder) and len(set(inorder)) == len(inorder)
return result
235. 二叉搜索树的最近公共祖先【剑指 Offer 68 - I. 二叉搜索树的最近公共祖先】
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 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, 因为根据定义最近公共祖先节点可以为节点本身。
说明:
- 所有节点的值都是唯一的。
- p、q 为不同节点且均存在于给定的二叉搜索树中。
方法一:迭代
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(p->val > q->val){
TreeNode* temp;
temp = p;
p = q;
q = temp;
}
while(true){
if(p->val <= root->val && root->val <= q->val){
return root;
}
if(root->val < p->val){
root = root->right;
}
if(root->val > q->val){
root = root->left;
}
}
}
};
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if p.val > q.val:
p, q = q, p
while True:
if p.val <= root.val <= q.val:
return root
if root.val < p.val:
root = root.right
if root.val > q.val:
root = root.left
方法二:递归
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(p->val > q->val){
TreeNode* temp;
temp = p;
p = q;
q = temp;
}
return dfs(root, p, q);
}
TreeNode* dfs(TreeNode* node, TreeNode* p, TreeNode* q){
if(!node){
return nullptr;
}
TreeNode* left = dfs(node->left, p, q);
TreeNode* right = dfs(node->right, p, q);
if(p->val <= node->val && node->val <= q->val){
return node;
}else if(node->val < p->val){
return right;
}else{
return left;
}
}
};
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if p.val > q.val:
p, q = q, p
return self.dfs(root, p, q)
def dfs(self, node, p, q):
if not node:
return None
left = self.dfs(node.left, p, q)
right = self.dfs(node.right, p, q)
if p.val <= node.val <= q.val:
return node
if node.val < p.val:
return right
if node.val > q.val:
return left
255. 验证前序遍历序列二叉搜索树
给定一个整数数组,你需要验证它是否是一个二叉搜索树正确的先序遍历序列。
你可以假定该序列中的数都是不相同的。
参考以下这颗二叉搜索树:
5
/ \
2 6
/ \
1 3
示例 1:
输入: [5,2,6,1,3]
输出: false
示例 2:
输入: [5,2,1,3,6]
输出: true
进阶挑战:您能否使用恒定的空间复杂度来完成此题?
方法一:单调栈
二叉搜索树的前序遍历:先递减,后递增
- 左子树是递减序列
- 右子树是递增序列
维护一个单调递减栈
我们可以通俗理解为“总体递增,局部递减”。为了达到“总体递增的效果”,我们要保证递减序列的第一个元素小于递减结束后即将递增的那个元素。因此,我们我们使用new_min和栈,如果递减结束后,下一个元素小于递减序列的第一个元素,违背了“总体递增”,立即返回False。
- BST的性质是左子树小于root,右子树大于root,所以校验这个性质即可。
- 单调递减栈的单调性用来假设root到左子树的性质正确,再用待push进栈的节点值必须大于已经pop出来的所有元素的值来校验root到右子树的性质。
- 得益于单调性,已经pop出来的所有元素的值是不断递增的,所以用最新的值来校验即可。
C++
class Solution {
public:
bool verifyPreorder(vector<int> &preorder) {
stack<int> st; // 维护一个单调递减栈
int root = INT_MIN; // 初始化升序序列部分的下限值
for (int num: preorder) {
if (num < root) { // 当开始升序后,要一直递增下去
return false;
}
while (!st.empty() && num > st.top()) { // 当 num > st.top() 时, 说明开始升序
root = st.top();
st.pop();
}
st.push(num);
}
return true;
}
};
Python
from typing import List
# 二分搜索树的前序遍历:先降序,后升序
class Solution:
# [10, 5, 2, 1, 3, 6, 8, 9]
def verifyPreorder(self, preorder: List[int]) -> bool:
stack = [] # 维护一个单调递减栈
new_min = float('-inf') # 初始化升序序列部分的下限值
for num in preorder:
# 当开始升序后,要一直递增下去
if num < new_min:
return False
while stack and num > stack[-1]: # 当 num > stack[-1] 时, 说明开始升序
new_min = stack.pop()
stack.append(num)
return True
solution = Solution()
nums = [10, 5, 2, 1, 3, 6, 11, 19] # [10, 5, 2, 1, 3, 6, 11, 19], [10, 5, 2, 1, 3, 11, 6, 19]
result = solution.verifyPreorder(nums)
print("result = ", result)
剑指 Offer 33. 二叉搜索树的后序遍历序列
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。
参考以下这颗二叉搜索树:
5
/ \
2 6
/ \
1 3
示例 1:
输入: [1,6,3,2,5]
输出: false
示例 2:
输入: [1,3,2,6,5]
输出: true
提示:数组长度 <= 1000
方法一:单调栈
维护一个单调递增栈
后序遍历倒序: [ 根节点 | 右子树 | 左子树 ] 。类似 先序遍历的镜像 ,即先序遍历为 “根、左、右” 的顺序,而后序遍历的倒序为 “根、右、左” 顺序。
在遍历过程中
- 我们将根节点压栈,然后遍历右子树,若是右子树存在,则序列的下一个数将大于根节点并且该结点就是右子树的根节点,同理压入栈中,以此类推,直到序列的下一个数小于根结点了,说明这棵子树没有右子树了,
- 但序列的下一个数并不一定是当前子树根结点的左孩子结点,也有可能是其父节点或祖父结点的左孩子结点,我们必须在祖先结点中找到比序列中下一个结点大的最小值作为序列中下一个结点的父节点(这里有点难理解,需结合二叉树 左<根<右 的性质和父子兄弟结点的大小关系思考为什么是这样),
- 所以我们将栈中结点依次出栈(因为栈中的是当前结点的一系列祖先结点),直到栈顶结点小于序列的下一个结点,
- 那么最后一个出栈的就是祖先结点中比序列中下一个结点大的最小结点,也就是序列中下一个结点的父节点,
- 我们再将序列下一个结点入栈,重复上述过程,最后就能根据后序遍历和BST的性质还原二叉搜索树
举个例子:[4, 8, 6, 12, 16, 14, 10]
10
/ \
6 14
/ \ / \
4 8 12 16
- 10作为根节点入栈,此时栈:[10]
- 14比栈顶元素10大,说明是10的右子树的根节点,入栈,此时栈:[10,14]
- 16比栈顶元素14大,说明是14的右子树的根节点,入栈,此时栈:[10,14,16]
- 12比栈顶元素16小,说明栈顶元素16没有右子树,这时我们要去栈中寻找12的父节点,即比12大的最小祖先结点
- 16,14均比12大,依次出栈,此时栈’[10]`
- 栈顶元素比12小,那么最后出栈的14就是比12大的最小祖先结点,即12的父节点,所以12是14的左孩子结点,最后将12入栈,此时栈[10,12]
- 6比栈顶元素12小,说明栈顶元素12没有右子树,依次将12,10出栈,此时栈空,所以最后出栈的结点10就是6的父节点,6是10的左孩子结点,此时栈 [6]
- 8比栈顶元素6大,说明是6的右子树的根节点,入栈,此时栈:[6,8]
- 4比8,6小,依次出栈,4是最后出栈元素6的左孩子结点
- 序列遍历结束,最后一个值无需再入栈了,得到如下二叉搜索树
class Solution {
public:
bool verifyPostorder(vector<int>& postorder) {
stack<int> stk;
int root = INT_MAX;
for(auto it = postorder.rbegin(); it != postorder.rend(); it++){
if(*it > root){
return false;
}
while(!stk.empty() && *it < stk.top()){
root = stk.top();
stk.pop();
}
stk.push(*it);
}
return true;
}
};
class Solution:
def verifyPostorder(self, postorder: [int]) -> bool:
stack = [] # 维护一个单调递增栈
root = float("+inf")
for num in reversed(postorder):
if num > root:
return False
while stack and num < stack[-1]: # 当 num < stack[-1] 时, 说明进入左子树
root = stack.pop()
stack.append(num)
return True
solution = Solution()
nums = [1, 3, 2, 6, 5] # [1, 3, 2, 6, 5], [1,6,3,2,5]
result = solution.verifyPostorder(nums)
print("result = ", result)
剑指 Offer 54. 二叉搜索树的第k大节点
给定一棵二叉搜索树,请找出其中第 k 大的节点的值。
示例 1:
输入: root = [3,1,4,null,2], k = 1
3
/ \
1 4
\
2
输出: 4
示例 2:
输入: root = [5,3,6,2,4,null,null,1], k = 3
5
/ \
3 6
/ \
2 4
/
1
输出: 4
限制:
- 1 ≤ k ≤ 二叉搜索树元素个数
中序遍历的倒序(设置变量result)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int idx = 0;
int result = 0;
int kthLargest(TreeNode* root, int k) {
dfs(root, k);
return result;
}
void dfs(TreeNode* node, int k){
if(!node){
return;
}
dfs(node->right, k);
idx++;
if(idx == k){
result = node->val;
}
dfs(node->left, k);
}
};
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def kthLargest(self, root: TreeNode, k: int) -> int:
self.idx = 0
self.result = None
self.dfs(root, k)
return self.result
def dfs(self, node, k):
if not node:
return
self.dfs(node.right, k)
self.idx += 1
if self.idx == k:
self.result = node.val
self.dfs(node.left, k)
中序遍历的倒序(不设置变量result)
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def kthLargest(self, root: TreeNode, k: int) -> int:
self.idx = 0
return self.dfs(root, k)
def dfs(self, node, k):
if not node:
return
r = self.dfs(node.right, k)
self.idx += 1
if self.idx == k:
return node.val
l = self.dfs(node.left, k)
return l or r
700-二叉搜索树中的搜索
给定二叉搜索树(BST)的根节点 root 和一个整数值 val。
你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。
示例 1:
给定二叉搜索树:
4
/ \
2 7
/ \
1 3
和值: 2
你应该返回如下子树:
2
/ \
1 3
在上述示例中,如果要找的值是 5,但因为没有节点值为 5,我们应该返回 NULL。
示例 2:
输入:root = [4,2,7,1,3], val = 5
输出:[]
提示:
- 数中节点数在 [1, 5000] 范围内
- 1 <= Node.val <= 1 0 7 10^7 107
- root 是二叉搜索树
- 1 <= val <= 1 0 7 10^7 107
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def searchBST(self, root: TreeNode, val: int) -> TreeNode:
def dfs(node, val):
# 空节点,返回
if node is None:
return None
# 查找到 返回 node
if node.val == val:
return node
# node.val > val 在左子树中查找
if node.val > val:
return dfs(node.left, val)
# node.val < val 在右子树中查找
if node.val < val:
return dfs(node.right, val)
return dfs(root, val)
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def searchBST(self, root: TreeNode, val: int) -> TreeNode:
return self.dfs(root, val)
def dfs(self, node, val):
if not node:
return None
if node.val == val:
return node
return self.dfs(node.left, val) or self.dfs(node.right, val)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
return dfs(root, val);
}
TreeNode* dfs(TreeNode* node, int val){
if(!node){
return nullptr;
}
if(node->val > val){ // 在左子树中查找
return dfs(node->left, val);
}else if(node->val < val){ // 在右子树中查找
return dfs(node->right, val);
}else{
return node;
}
}
};
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def searchBST(self, root: TreeNode, val: int) -> TreeNode:
if not root:
return None
if root.val > val:
return self.searchBST(root.left, val)
elif root.val < val:
return self.searchBST(root.right, val)
else:
return root
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if(!root){
return nullptr;
}
if(root->val > val){ // 在左子树中查找
return searchBST(root->left, val);
}else if(root->val < val){ // 在右子树中查找
return searchBST(root->right, val);
}else{
return root;
}
}
};
783-二叉搜索树节点最小距离【530-二叉搜索树的最小绝对差 】
给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。
示例 1:
输入:root = [4,2,6,1,3]
输出:1
示例 2:
输入:root = [1,0,48,null,null,12,49]
输出:1
提示:
- 树中节点数目在范围 [2, 100] 内
- 0 < = N o d e . v a l < = 1 0 5 0 <= Node.val <= 10^5 0<=Node.val<=105
- 差值是一个正数,其数值等于两值之差的绝对值
方法:中序遍历
- 要求 BST 的任意两个不同节点之间的最小差值,也就是相当于求 BST 中序遍历得到的有序序列中所有相邻节点之间的最小差值。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def minDiffInBST(self, root: TreeNode) -> int:
inorderList = []
def inorder(node):
if node is None:
return
if node.left is not None:
inorder(node.left)
inorderList.append(node.val)
if node.right is not None:
inorder(node.right)
inorder(root)
result = min([inorderList[i] - inorderList[i - 1] for i in range(1, len(inorderList))])
return result
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> result;
int getMinimumDifference(TreeNode* root) {
inOrder(root);
int answer = INT_MAX;
for(auto it = result.begin() + 1; it != result.end(); it++){
answer = min(answer, *it - *(it - 1));
}
return answer;
}
void inOrder(TreeNode* node){
if(!node){
return;
}
inOrder(node->left);
result.push_back(node->val);
inOrder(node->right);
}
};
501-二叉搜索树中的众数
给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。
假定 BST 有如下定义:
- 结点左子树中所含结点的值小于等于当前结点的值
- 结点右子树中所含结点的值大于等于当前结点的值
- 左子树和右子树都是二叉搜索树
例如:
给定 BST [1,null,2,2],
1
\
2
/
2
返回[2].
提示:如果众数超过1个,不需考虑输出顺序
进阶:你可以不使用额外的空间吗?(假设由递归产生的隐式调用栈的开销不被计算在内)
方法一:利用字典
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def findMode(self, root: TreeNode) -> List[int]:
dict = {}
def preorder(node):
if node is None:
return
dict[node.val] = dict.get(node.val, 0) + 1
if node.left is not None:
preorder(node.left)
if node.right is not None:
preorder(node.right)
preorder(root)
print("dict = ", dict)
mode = max(dict.values())
print("mode = ", mode)
result = []
for key in dict.keys():
if dict[key] == mode:
result.append(key)
return result
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
unordered_map<int, int> mp;
vector<int> findMode(TreeNode* root) {
dfs(root);
auto max_val = max_element(mp.begin(), mp.end(), [](pair<int, int> left, pair<int, int> right){return left.second < right.second;})->second; // 获取mp中value最大值
std::cout << "max_val = " << max_val << std::endl;
vector<int> result;
for(auto [key, value] : mp){
if(value == max_val){
result.push_back(key);
}
}
return result;
}
void dfs(TreeNode* node){
if(!node){
return;
}
mp[node->val]++;
dfs(node->left);
dfs(node->right);
}
};
230-二叉搜索树中第K小的元素
给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。
示例 1:
输入:root = [3,1,4,null,2], k = 1
输出:1
示例 2:
输入:root = [5,3,6,2,4,null,null,1], k = 3
输出:3
提示:
- 树中的节点数为 n 。
- 1 < = k < = n < = 1 0 4 1 <= k <= n <= 10^4 1<=k<=n<=104
- 0 < = N o d e . v a l < = 1 0 4 0 <= Node.val <= 10^4 0<=Node.val<=104
进阶:如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k 小的值,你将如何优化算法?
方法一:中序遍历【二分搜索树的中序遍历是升序列表】
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right.
# 二分搜索树的中序遍历是升序列表
class Solution:
def kthSmallest(self, root: TreeNode, k: int) -> int:
inorderList = []
def inorder(node):
if node is None:
return None
inorder(node.left)
inorderList.append(node.val)
inorder(node.right)
inorder(root)
return inorderList[k - 1]
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> inorderList;
int kthSmallest(TreeNode* root, int k) {
inorder(root);
return inorderList[k - 1];
}
void inorder(TreeNode* node){
if(node == nullptr){
return;
}
inorder(node->left);
inorderList.push_back(node->val);
inorder(node->right);
}
};
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int kthSmallest(TreeNode* root, int k) {
vector<int> inorderList;
inorder(root, inorderList);
return inorderList[k - 1];
}
void inorder(TreeNode* node, vector<int> &inorderList){
if(!node){
return;
}
inorder(node->left, inorderList);
inorderList.push_back(node->val);
inorder(node->right, inorderList);
}
};
方法二:中序遍历
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right.
class Solution:
result = None
def kthSmallest(self, root: TreeNode, k: int) -> int:
def inorder(root):
nonlocal k
if root.left:
inorder(root.left)
# 逻辑主体部分
k -= 1
if k == 0:
self.result = root.val
return
if root.right:
inorder(root.right)
inorder(root)
return self.result
面试题 04.06. 后继者
设计一个算法,找出二叉搜索树中指定节点的“下一个”节点(也即中序后继)。
如果指定节点没有对应的“下一个”节点,则返回null。
示例 1:
输入: root = [2,1,3], p = 1
2
/ \
1 3
输出: 2
示例 2:
输入: root = [5,3,6,2,4,null,null,1], p = 6
5
/ \
3 6
/ \
2 4
/
1
输出: null
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* result = nullptr;
TreeNode* prev = nullptr;
TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
dfs(root, p);
return result;
}
void dfs(TreeNode* node, TreeNode* p){
if(!node){
return;
}
dfs(node->left, p);
if(prev == p){
result = node;
}
prev = node;
dfs(node->right, p);
}
};
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def inorderSuccessor(self, root: TreeNode, p: TreeNode) -> TreeNode:
self.prev = None
self.result = None
self.dfs(root, p)
return self.result
def dfs(self, node, p):
if not node:
return
self.dfs(node.left, p)
if self.prev == p:
self.result = node
self.prev = node
self.dfs(node.right, p)
二、二叉搜索树的修改与构造
108-将有序数组转换为二叉搜索树
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。
高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。
示例 1:
输入:nums = [-10,-3,0,5,9]
输出:[0,-3,9,-10,null,5]
解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:
示例 2:
输入:nums = [1,3]
输出:[3,1]
解释:[1,3] 和 [3,1] 都是高度平衡二叉搜索树。
提示:
- 1 < = n u m s . l e n g t h < = 1 0 4 1 <= nums.length <= 10^4 1<=nums.length<=104
- − 1 0 4 < = n u m s [ i ] < = 1 0 4 -10^4 <= nums[i] <= 10^4 −104<=nums[i]<=104
- nums 按 严格递增 顺序排列
方法一:中序遍历,总是选择中间位置左边的数字作为根节点
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
def make_tree(start_index, end_index): #只和长度有关
#首先判定我们的区间是否合理,即left_index要<=right_index
#当相等时,只有root会产生,不会产生左右小树
if start_index > end_index:
return None
#我这里变量名都写得比较长,目的是方便理解
mid_index = (start_index + end_index)//2
this_tree_root = TreeNode(nums[mid_index]) #做一个小树的root
this_tree_root.left = make_tree(start_index,mid_index-1)
this_tree_root.right = make_tree(mid_index+1, end_index)
return this_tree_root #做好的小树
return make_tree(0,len(nums)-1)
#可以看到整个题解只和index有关,和数组里的具体数字无关,
#因为题目给出的“有序数列”帮助我们满足了“二叉搜索树”的条件。
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
def make_tree(left, right):
if left > right:
return None
# 总是选择中间位置左边的数字作为根节点
mid = (left + right) // 2
root = TreeNode(nums[mid])
root.left = make_tree(left, mid - 1)
root.right = make_tree(mid + 1, right)
return root
return make_tree(0, len(nums) - 1)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
return make_tree(nums, 0, nums.size() - 1);
}
TreeNode* make_tree(vector<int>& nums, int left, int right) {
if (left > right) {
return nullptr;
}
// 总是选择中间位置左边的数字作为根节点
int mid = (left + right) / 2;
TreeNode* root = new TreeNode(nums[mid]);
root->left = make_tree(nums, left, mid - 1);
root->right = make_tree(nums, mid + 1, right);
return root;
}
};
方法二:中序遍历,总是选择中间位置右边的数字作为根节点
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
def make_tree(left, right):
if left > right:
return None
# 总是选择中间位置右边的数字作为根节点
mid = (left + right + 1) // 2
root = TreeNode(nums[mid])
root.left = make_tree(left, mid - 1)
root.right = make_tree(mid + 1, right)
return root
return make_tree(0, len(nums) - 1)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
return make_tree(nums, 0, nums.size() - 1);
}
TreeNode* make_tree(vector<int>& nums, int left, int right) {
if (left > right) {
return nullptr;
}
// 总是选择中间位置右边的数字作为根节点
int mid = (left + right + 1) / 2;
TreeNode* root = new TreeNode(nums[mid]);
root->left = make_tree(nums, left, mid - 1);
root->right = make_tree(nums, mid + 1, right);
return root;
}
};
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
return make_tree(nums, 0, nums.size() - 1);
}
TreeNode* make_tree(vector<int>& nums, int left, int right) {
if (left > right) {
return nullptr;
}
// 总是选择中间位置右边的数字作为根节点
int mid = (left + right + 1) / 2;
auto root = new TreeNode(nums[mid]);
root->left = make_tree(nums, left, mid - 1);
root->right = make_tree(nums, mid + 1, right);
return root;
}
};
109-有序链表转换二叉搜索树
给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定的有序链表: [-10, -3, 0, 5, 9],
一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
方法一:快慢指针找到链表中间节点
和 108 思路一样。 不同的是数据结构的不同,因此我们需要关注的是链表和数组的操作差异。
数组的情况:
我们再来看下链表:
找到中点,只需要使用经典的快慢指针即可。同时为了防止环的出现, 我们需要斩断指向 mid 的 next 指针,因此需要记录一下中点前的一个节点,这只需要用一个变量 pre 记录即可。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* sortedListToBST(ListNode* head) {
if(!head){
return nullptr;
}
if(!head->next){
return new TreeNode(head->val);
}
auto prev = new ListNode();
auto slow = head;
auto fast = head;
// 快慢指针找到链表中间节点
while(fast && fast->next){
prev = slow;
slow = slow->next;
fast = fast->next->next;
}
// 斩断左侧链表
if(prev){
prev->next = nullptr;
}
// 使用中间节点作为root
auto root = new TreeNode(slow->val);
root->left = sortedListToBST(head);
root->right = sortedListToBST(slow->next);
return root;
}
};
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedListToBST(self, head: Optional[ListNode]) -> Optional[TreeNode]:
if not head:
return None
if not head.next:
return TreeNode(head.val)
slow = head
fast = head
prev = None
# 快慢指针找到链表中间节点
while fast and fast.next:
prev = slow
slow = slow.next
fast = fast.next.next
# 斩断左侧链表
if prev:
prev.next = None
# 使用中间节点作为root
root = TreeNode(slow.val)
root.left = self.sortedListToBST(head)
root.right = self.sortedListToBST(slow.next)
return root
方法二:将有序链表转成有序数组【然后参考“## 108-将有序数组转换为二叉搜索树 ”】
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedListToBST(self, head: ListNode) -> TreeNode:
nums = []
while head:
nums.append(head.val)
head = head.next
def makeTree(left, right):
if left > right:
return None
mid = left + (right - left) // 2
root = TreeNode(nums[mid])
root.left = makeTree(left, mid - 1)
root.right = makeTree(mid + 1, right)
return root
return makeTree(0, len(nums) - 1)
1008. 前序遍历构造二叉搜索树
返回与给定前序遍历 preorder 相匹配的二叉搜索树(binary search tree)的根结点。
(回想一下,二叉搜索树是二叉树的一种,其每个节点都满足以下规则,对于 node.left 的任何后代,值总 < node.val,而 node.right 的任何后代,值总 > node.val。此外,前序遍历首先显示节点 node 的值,然后遍历 node.left,接着遍历 node.right。)
题目保证,对于给定的测试用例,总能找到满足要求的二叉搜索树。
示例:
输入:[8,5,1,7,10,12]
输出:[8,5,10,1,7,null,12]
提示:
- 1 <= preorder.length <= 100
- 1 <= preorder[i] <= 1 0 8 10^8 108
- preorder 中的值互不相同
538-把二叉搜索树转换为累加树
给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。
提醒一下,二叉搜索树满足下列约束条件:
- 节点的左子树仅包含键 小于 节点键的节点。
- 节点的右子树仅包含键 大于 节点键的节点。
- 左右子树也必须是二叉搜索树。
注意:本题和 1038: https://leetcode-cn.com/problems/binary-search-tree-to-greater-sum-tree/ 相同
示例 1:
输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]
输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]
示例 2:
输入:root = [0,null,1]
输出:[1,null,1]
示例 3:
输入:root = [1,0,2]
输出:[3,3,2]
示例 4:
输入:root = [3,2,4,1]
输出:[7,9,4,10]
提示:
- 树中的节点数介于 0 和 1 0 4 10^4 104 之间。
- 每个节点的值介于 − 1 0 4 -10^4 −104 和 1 0 4 10^4 104 之间。
- 树中的所有值 互不相同 。
- 给定的树为二叉搜索树。
方法一:反中序遍历
本题中要求我们将每个节点的值修改为原来的节点值加上所有大于它的节点值之和。这样我们只需要反序中序遍历该二叉搜索树,记录过程中的节点值之和,并不断更新当前遍历到的节点的节点值,即可得到题目要求的累加树。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
self.total = 0
self.dfs(root)
return root
def dfs(self, node):
if not node:
return
self.dfs(node.right)
self.total += node.val
node.val = self.total
self.dfs(node.left)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int total = 0;
TreeNode* convertBST(TreeNode* root) {
dfs(root);
return root;
}
void dfs(TreeNode* node){
if(!node){
return;
}
dfs(node->right);
total += node->val;
node->val = total;
dfs(node->left);
}
};
701-二叉搜索树中的插入操作
给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
示例 1:
输入:root = [4,2,7,1,3], val = 5
输出:[4,2,7,1,3,5]
解释:另一个满足题目要求可以通过的树是:
示例 2:
输入:root = [40,20,60,10,30,50,70], val = 25
输出:[40,20,60,10,30,50,70,null,null,25]
示例 3:
输入:root = [4,2,7,1,3,null,null,null,null,null,null], val = 5
输出:[4,2,7,1,3,5]
提示:
- 给定的树上的节点数介于 0 和 1 0 4 10^4 104 之间
- 每个节点都有一个唯一整数值,取值范围从 0 到 1 0 8 10^8 108
- − 1 0 8 < = v a l < = 1 0 8 -10^8 <= val <= 10^8 −108<=val<=108
- 新值和原始二叉搜索树中的任意节点值都不同
递归方法:
- 我们只需要确定val是要放在左子树还是右子树即可。递归结束条件是 node == None.
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(!root){
return new TreeNode(val);
}
if(val < root->val){
root->left = insertIntoBST(root->left, val);
}
if(val > root->val){
root->right = insertIntoBST(root->right, val);
}
return root;
}
};
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
# 如果当前节点为空,也就意味着val找到了合适的位置,此时创建节点直接返回。
if root is None:
return TreeNode(val)
# 递归创建左子树
if val < root.val:
root.left = self.insertIntoBST(root.left, val)
# 递归创建右子树
if val > root.val:
root.right = self.insertIntoBST(root.right, val)
return root
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
def dfs(node: TreeNode, val:int)->TreeNode:
# 如果当前节点为空,也就意味着val找到了合适的位置,此时创建节点直接返回。
if node is None:
return TreeNode(val)
# 递归创建左子树
if val < node.val:
node.left = dfs(node.left, val)
# 递归创建右子树
if val > node.val:
node.right = dfs(node.right, val)
return node
return dfs(root, val)
迭代方法:
class Solution:
def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
if not root:
return TreeNode(val)
parent = None
cur = root
# 用while循环不断地找新节点的parent
while cur:
if cur.val < val:
parent = cur
cur = cur.right
elif cur.val > val:
parent = cur
cur = cur.left
# 运行到这意味着已经跳出上面的while循环,
# 同时意味着新节点的parent已经被找到.
# parent已被找到, 新节点已经ready. 把两个节点黏在一起就好了.
if parent.val > val:
parent.left = TreeNode(val)
else:
parent.right = TreeNode(val)
return root
450-删除二叉搜索树中的节点
给定一个二叉搜索树的根节点 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
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def deleteNode(self, root: TreeNode, key: int) -> TreeNode:
def getSuccessor(node):
"""获取node的后继节点"""
node = node.right # 定位到node右子树
while node.left: # 寻找右子树中最靠左的节点
node = node.left
return node
def dfs(node: TreeNode, key: int)->TreeNode:
"""
删除具有key值的节点,并返回删除后的根节点
"""
if not node:
return None
# 最外层的if...elif...else用于搜索待删除结点
if node.val > key: # 待删除结点值小于根节点,位于根节点左子树
node.left = dfs(node.left, key) # 递归删除左子树,并返回删除后的左子树
elif node.val < key: # 待删除结点值大于根节点,位于根节点右子树
node.right = dfs(node.right, key) # 递归删除右子树,并返回删除后的右子树
else: # 待删除节点为根节点
if node.left is None and node.right is None: # 对应情况1,待删除结点没有子节点
node = None # 将该节点置空
elif node.left is not None and node.right is None: # 对应情况2,待删除节点只有左节点
node = node.left # 用左节点代替该节点
elif node.left is None and node.right is not None: # 对应情况3,待删除节点只有右节点
node = node.right # 用右节点代替该节点
else: # 对应情况3,待删除节点有左右两个节点
succ = getSuccessor(node) # 找到后继节点
node.val = succ.val # 将值替换为后继节点的值
node.right = dfs(node.right, succ.val) # 删除没用的后继节点
return node
return dfs(root, key)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
return dfs(root, key);
}
// 删除具有key值的节点,并返回删除后的根节点
TreeNode* dfs(TreeNode* node, int key){
if(!node){
return nullptr;
}
// 最外层的if...elif...else用于搜索待删除结点
if(node->val > key){ // 待删除结点值小于根节点,位于根节点左子树
node->left = dfs(node->left, key); // 递归删除左子树,并返回删除后的左子树
}else if(node->val < key){ // 待删除结点值大于根节点,位于根节点右子树
node->right = dfs(node->right, key); // 递归删除右子树,并返回删除后的右子树
}else{ // 待删除节点为根节点
if(!node->left && !node->right){ // 对应情况1,待删除结点没有子节点
node = nullptr; // 将该节点置空
}else if(node->left && !node->right){ // 对应情况2,待删除节点只有左节点
node = node->left; // 用左节点代替该节点
}else if(!node->left && node->right){ // 对应情况3,待删除节点只有右节点
node = node->right; // 用右节点代替该节点
}else{ // 对应情况3,待删除节点有左右两个节点
auto succ = getSuccessor(node); // 找到后继节点
node->val = succ->val; // 将值替换为后继节点的值
node->right = dfs(node->right, succ->val); // 删除没用的后继节点
}
}
return node;
}
// 获取node的后继节点
TreeNode* getSuccessor(TreeNode* node){
node = node->right; // 定位到node右子树
while(node->left){ // 寻找右子树中最靠左的节点
node = node->left;
}
return node;
}
};
669-修剪二叉搜索树
给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树不应该改变保留在树中的元素的相对结构(即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在唯一的答案。
所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。
示例 1:
输入:root = [1,0,2], low = 1, high = 2
输出:[1,null,2]
示例 2:
输入:root = [3,0,4,null,2,null,null,1], low = 1, high = 3
输出:[3,2,null,1]
示例 3:
输入:root = [1], low = 1, high = 2
输出:[1]
示例 4:
输入:root = [1,null,2], low = 1, high = 3
输出:[1,null,2]
示例 5:
输入:root = [1,null,2], low = 2, high = 4
输出:[2]
提示:
- 树中节点数在范围 [ 1 , 1 0 4 ] [1, 10^4] [1,104] 内
- 0 < = N o d e . v a l < = 1 0 4 0 <= Node.val <= 10^4 0<=Node.val<=104
- 树中每个节点的值都是唯一的
- 题目数据保证输入是一棵有效的二叉搜索树
- 0 < = l o w < = h i g h < = 1 0 4 0 <= low <= high <= 10^4 0<=low<=high<=104
对于二叉数的递归,我们只要想清楚当前的root需要干什么就行!
递归函数递归到当前节点,有三种情况:
- 第一种情况:L<= node.val <= R,则不需要将当前节点删除,只需要递归去调用函数裁剪左右子树即可;
- 第二种情况: node.val < L,则左边子树和当前节点都要删掉,此时用node.right的递归结果顶替node即可;
- 第三种情况: node.val > R,则右边子树和当前节点都要删掉,此时用node.left的递归结果顶替node即可;
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def trimBST(self, root: TreeNode, low: int, high: int) -> TreeNode:
def dfs(node):
if not node:
return None
if node.val < low: # 如果当前节点值小于左边界,则该节点左子树全小于左边界,应该被删除
node = dfs(node.right)
elif node.val > high: # 如果当前节点值大于右边界,则该节点右子树全大于右边界,应该被删除
node = dfs(node.left)
else: # 在界限中,则往左右子树搜
node.left = dfs(node.left)
node.right = dfs(node.right)
return node
return dfs(node)
# Definition for a binary tree root.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def trimBST(self, root: TreeNode, low: int, high: int) -> TreeNode:
if not root:
return None
if root.val < low: # 如果当前节点值小于左边界,则该节点左子树全小于左边界,应该被删除
root = self.trimBST(root.right, low, high)
elif root.val > high: # 如果当前节点值大于右边界,则该节点右子树全大于右边界,应该被删除
root = self.trimBST(root.left, low, high)
else: # 在界限中,则往左右子树搜
root.left = self.trimBST(root.left, low, high)
root.right = self.trimBST(root.right, low, high)
return root
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
if(!root){
return nullptr;
}
if(root->val < low){ // 如果当前节点值小于左边界,则该节点左子树全小于左边界,应该被删除,只需要递归剪枝右子树
root = trimBST(root->right, low, high);
}else if(root->val > high){ // 如果当前节点值大于右边界,则该节点右子树全大于右边界,应该被删除,只需要递归剪枝左子树
root = trimBST(root->left, low, high);
}else{ // 在界限中,则往左右子树同时递归剪枝
root->left = trimBST(root->left, low, high);
root->right = trimBST(root->right, low, high);
}
return root;
}
};
426. 将二叉搜索树转化为排序的双向链表【剑指 Offer 36. 二叉搜索树与双向链表】
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:
我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node() {}
Node(int _val) {
val = _val;
left = NULL;
right = NULL;
}
Node(int _val, Node* _left, Node* _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
public:
Node* prev = nullptr;
Node* head = nullptr;
Node* treeToDoublyList(Node* root) {
if(!root){
return nullptr;
}
dfs(root);
prev->right = head;
head->left = prev;
return head;
}
void dfs(Node* node){
if(!node){
return;
}
dfs(node->left);
if(!prev){
head = node;
}else{
prev->right = node;
node->left = prev;
}
prev = node;
dfs(node->right);
}
};
"""
# Definition for a Node.
class Node:
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
"""
class Solution:
def treeToDoublyList(self, root: 'Node') -> 'Node':
self.prev = None
if not root:
return None
self.dfs(root)
self.prev.right = self.head
self.head.left = self.prev
return self.head
def dfs(self, node):
if not node:
return
self.dfs(node.left)
if self.prev is None:
self.head = node
else:
self.prev.right = node
node.left = self.prev
self.prev = node
self.dfs(node.right)