目录
力扣 98. 验证二叉搜索树 面试题 04.05. 合法二叉搜索树
一,二叉搜索树(二叉查找树,二叉排序树,BST)
二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
二,二叉搜索树的性质
1,二叉搜索树的中序遍历结果,一定是一个升序序列。
2,二叉搜索树的中序遍历结果中,任意相邻的两个节点,至少有一个节点的孩子数不超过1,更严格来讲,要么左边节点没有右孩子,要么右边节点没有左孩子。
三,查询
二叉搜索树的查询非常简单
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
};
TreeNode* find(TreeNode* root, int val)
{
if(!root)return NULL;
if(root->val==val)return root;
if(root->val>val)return find(root->left,val);
return find(root->right,val);
}
//find first node.val > val
Treenode* findBig(Treenode* root, int val)
{
if (!root)return NULL;
if (root->val <= val)return findBig(root->right, val);
auto ans = findBig(root->left, val);
return ans? ans:root;
}
//find last node.val < val
Treenode* findSmall(Treenode* root, int val)
{
if (!root)return NULL;
if (root->val >= val)return findSmall(root->left, val);
auto ans = findSmall(root->right, val);
return ans ? ans : root;
}
3个查找函数分别查找等于val的唯一一个元素、大于val的第一个元素、小于val的最后一个元素,都用NULL返回值表示找不到。
四,插入节点
根据二叉搜索树的性质,中序遍历结果中任意相邻的两个节点,要么左边节点没有右孩子,要么右边节点没有左孩子,只要在这个空位上插入新节点即可。
换句话说,有这么一种算法,可以保证插入新节点后,它一定会是一个叶子节点,直接去掉这个叶子节点得到的树就是原树。
想通这一点后,递归的代码就变得非常简单。
代码:
void InsertNode(TreeNode* &root, TreeNode* node)
{
if(!root)root=node;
else if(root->val>node->val)InsertNode(root->left,node);
else InsertNode(root->right,node);
}
void InsertNode(TreeNode* &root, int val)
{
TreeNode* p = find(root,val);
if(p)return;
p= new TreeNode();
p->val=val;
p->left=NULL, p->right=NULL;
InsertNode(root,p);
}
我们用下文力扣 98. 验证二叉搜索树里面的代码,校验插入结果是否为BST,再用中序遍历结果辅助校验,确认节点都插入成功。
代码:
bool isValidBST(TreeNode* root, long long low, long long high){
if (!root)return true;
if (root->val <= low || root->val >= high)return false;
return isValidBST(root->left, low, root->val) && isValidBST(root->right, root->val, high);
}
bool isValidBST(TreeNode* root) {
return isValidBST(root, -1000, 1000);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int>v1;
if (root == NULL)return v1;
v1 = inorderTraversal(root->left);
v1.insert(v1.end(), root->val);
vector<int>v2 = inorderTraversal(root->right);
v1.insert(v1.end(), v2.begin(), v2.end());
return v1;
}
//输出一维vector
template<typename T>
void fcout(vector<T>&v)
{
for(int i=0;i<v.size();i++)cout<<v[i]<<" ";
cout<<endl;
}
int main()
{
TreeNode* root=NULL;
InsertNode(root,2);
InsertNode(root,5);
InsertNode(root,1);
InsertNode(root,4);
InsertNode(root,2); //重复
InsertNode(root,8);
InsertNode(root,5); //重复
InsertNode(root,3);
InsertNode(root,6);
cout<<int(isValidBST(root))<<endl;
vector<int>ret=inorderTraversal(root);
fcout(ret);
TreeNode* p =find(root,3);
cout<<p->val;
return 0;
}
运行结果:
1
1 2 3 4 5 6 8
3
五,删除节点
1,拷贝
删除节点的时候会涉及到节点的拷贝,而拷贝有2种做法,一种是值拷贝,一种是把节点之间的互链重链。
为了思路清晰,我这里考虑的是两种方案都算拷贝。
对于要求不能用值拷贝的方法,我下面的思路还不能完全适用。
2,删除根节点
因为可以接受值拷贝,所以删除一个节点A,只需要考虑对于以A为根的树,删掉A并换上新的根节点即可。
所以我后面的测试也是不停的删除根节点,看看结果是否正常。
3,删除根节点的思路
按照情况分类,根据待删节点有没有左孩子,有没有右孩子,可以分成三种情况
(1)没有孩子,直接删除即可
(2)只有一个孩子,用孩子代替它即可
(3)左孩子和右孩子都有的情况下,找出右子树中的最小节点,它就是中序遍历序列中待删节点的后继节点,用这个后继节点替换待删节点即可。
其中,还需要根据后继节点是不是待删节点的孩子分两种情况处理。
代码:
TreeNode* GetMin(TreeNode* root)
{
if (!root)return root;
if (root->left)return GetMin(root->left);
return root;
}
TreeNode* GetParent(TreeNode* root, TreeNode* node)
{
if (!root || root == node || root->left == node || root->right == node)return root;
if (root->val > node->val)return GetParent(root->left, node);
return GetParent(root->right, node);
}
void DeleteNode(TreeNode*& node)
{
if (node->left == NULL) {
node = node->right;
return;
}
if (node->right == NULL) {
node = node->left;
return;
}
TreeNode* thenext = GetMin(node->right);
if (thenext == node->right) {
thenext->left = node->left, node = thenext;
return;
}
node->val = thenext->val;
TreeNode* p = GetParent(node, thenext);
p->left = thenext->right;
}
这个代码,稍微改一下就可以适用于不能用值拷贝的场景。
测试代码:
void DeleteAndJudge(TreeNode* &root)
{
DeleteNode(root);
cout << endl << int(isValidBST(root)) << endl;
vector<int>ret = inorderTraversal(root);
fcout(ret);
}
int main()
{
TreeNode* root = NULL;
InsertNode(root, 2);
InsertNode(root, 5);
InsertNode(root, 1);
InsertNode(root, 4);
InsertNode(root, 8);
InsertNode(root, 3);
InsertNode(root, 6);
cout << int(isValidBST(root)) << endl;
vector<int>ret = inorderTraversal(root);
fcout(ret);
DeleteAndJudge(root);
DeleteAndJudge(root);
DeleteAndJudge(root);
DeleteAndJudge(root);
DeleteAndJudge(root);
DeleteAndJudge(root);
return 0;
}
运行结果:
1
1 2 3 4 5 6 8
1
1 3 4 5 6 8
1
1 4 5 6 8
1
1 5 6 8
1
1 6 8
1
1 8
1
1
六,OJ实战
力扣 98. 验证二叉搜索树 面试题 04.05. 合法二叉搜索树
题目:
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:
2
/ \
1 3
输出: true
示例 2:
输入:
5
/ \
1 4
/ \
3 6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。
代码:
class Solution {
public:
bool isValidBST(TreeNode* root, long long low, long long high){
if (!root)return true;
if (root->val <= low || root->val >= high)return false;
return isValidBST(root->left, low, root->val) && isValidBST(root->right, root->val, high);
}
bool isValidBST(TreeNode* root) {
return isValidBST(root, -3312345678, 3312345678);
}
};
力扣 255. 验证前序遍历序列二叉搜索树
给定一个整数数组,你需要验证它是否是一个二叉搜索树正确的先序遍历序列。
你可以假定该序列中的数都是不相同的。
参考以下这颗二叉搜索树:
5
/ \
2 6
/ \
1 3
示例 1:
输入: [5,2,6,1,3]
输出: false
示例 2:
输入: [5,2,1,3,6]
输出: true
进阶挑战:
您能否使用恒定的空间复杂度来完成此题?
思路:
按照快速排序的划分方式进行分治是不行的,最坏是O(n^2),会超时
一种改进思路是二分法,这个思路我没尝试,时间复杂度O(nlogn)
另外一种思路是先离散化,把数据映射成0,1,2,3,4... 然后根据数值就可以直接划分,时间复杂度O(nlogn)
class Solution {
public:
bool verifyPreorder(vector<int>& p,int low,int high,int nl,int nh) {
if(low>high)return true;
if(p[low]<nl||p[low]>nh)return false;
return verifyPreorder(p,low+1,low+p[low]-nl, nl,p[low]-1) &&
verifyPreorder(p,low+p[low]-nl+1, high,p[low]+1,nh);
}
bool verifyPreorder(vector<int>& preorder) {
if(preorder.size()==0)return true;
preorder=sortId2(preorder);
return verifyPreorder(preorder,0,preorder.size()-1,0,preorder.size()-1);
}
};
力扣 285. 二叉搜索树中的中序后继
给定一棵二叉搜索树和其中的一个节点 p ,找到该节点在树中的中序后继。如果节点没有中序后继,请返回 null 。
节点 p 的后继是值比 p.val 大的节点中键值最小的节点。
示例 1:
输入:root = [2,1,3], p = 1
输出:2
解释:这里 1 的中序后继是 2。请注意 p 和返回值都应是 TreeNode 类型。
示例 2:
输入:root = [5,3,6,2,4,null,null,1], p = 6
输出:null
解释:因为给出的节点没有中序后继,所以答案就返回 null 了。
提示:
树中节点的数目在范围 [1, 104] 内。
-105 <= Node.val <= 105
树中各节点的值均保证唯一。
class Solution {
public:
TreeNode* getParent(TreeNode* root, TreeNode* p) {
if(!root)return NULL;
if(root->left==p||root->right==p)return root;
TreeNode* q=getParent(root->left,p);
if(q)return q;
return getParent(root->right,p);
}
TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
if(p->right){
p=p->right;
while(p->left)p=p->left;
return p;
}
TreeNode* q;
while(true) {
q=getParent(root, p);
if(!q)return NULL;
if(q->left==p)return q;
p=q;
}
}
};
我这个时间复杂度不好,是O(n^2)的,n是节点数目。
可以优化成O(n)的算法。
力扣 510. 二叉搜索树中的中序后继 II
给定一棵二叉搜索树和其中的一个节点 node ,找到该节点在树中的中序后继。如果节点没有中序后继,请返回 null 。
一个节点 node 的中序后继是键值比 node.val 大所有的节点中键值最小的那个。
你可以直接访问结点,但无法直接访问树。每个节点都会有其父节点的引用。节点 Node 定义如下:
class Node {
public int val;
public Node left;
public Node right;
public Node parent;
}
示例 1:
输入:tree = [2,1,3], node = 1
输出:2
解析:1 的中序后继结点是 2 。注意节点和返回值都是 Node 类型的。
示例 2:
输入:tree = [5,3,6,2,4,null,null,1], node = 6
输出:null
解析:该结点没有中序后继,因此返回 null 。
示例 3:
输入:tree = [15,6,18,3,7,17,20,2,4,null,13,null,null,null,null,null,null,null,null,9], node = 15
输出:17
示例 4:
输入:tree = [15,6,18,3,7,17,20,2,4,null,13,null,null,null,null,null,null,null,null,9], node = 13
输出:15
示例 5:
输入:tree = [0], node = 0
输出:null
提示:
树中节点的数目在范围 [1, 104] 内。
-105 <= Node.val <= 105
树中各结点的值均保证唯一。
进阶:你能否在不访问任何结点的值的情况下解决问题?
class Solution {
public:
Node* inorderSuccessor(Node* p) {
if(p->right){
p=p->right;
while(p->left)p=p->left;
return p;
}
Node* q;
while(true) {
q=p->parent;
if(!q)return NULL;
if(q->left==p)return q;
p=q;
}
}
};
力扣 96. 不同的二叉搜索树
题目:
给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?
示例:
输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
思路:
递推式f(n)=f(n-1)+f(1)f(n-2)+...+f(n-2)f(1)+f(n-1)
代码:
class Solution {
public:
int numTrees(int n) {
if (n <= 0)return 1;
static map<int, int>ans;
if (ans[n])return ans[n];
int res = 0;
for (int i = 0; i < n; i++)res += numTrees(i)*numTrees(n - 1 - i);
return ans[n] = res;
}
};
力扣 95. 不同的二叉搜索树 II
题目:
给定一个整数 n,生成所有由 1 ... n 为节点所组成的二叉搜索树。
示例:
输入: 3
输出:
[
[1,null,3,2],
[3,2,null,1],
[3,1,null,null,2],
[2,1,3],
[1,null,2,null,3]
]
解释:
以上的输出对应以下 5 种不同结构的二叉搜索树:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
代码:
class Solution {
public:
vector<TreeNode*> generateTrees(int low,int high) {
vector<TreeNode*>ans;
if (low > high)
{
ans.insert(ans.end(), NULL);
return ans;
}
if (low == high)
{
TreeNode *p = new TreeNode(low);
ans.insert(ans.end(), p);
return ans;
}
for (int i = low; i <= high; i++)
{
vector<TreeNode*>v1 = generateTrees(low, i - 1);
vector<TreeNode*>v2 = generateTrees(i + 1, high);
for (int k = 0; k < v1.size(); k++)
{
for (int j = 0; j < v2.size(); j++)
{
TreeNode *p = new TreeNode(i);
p->left = v1[k], p->right = v2[j];
ans.insert(ans.end(), p);
}
}
}
return ans;
}
vector<TreeNode*> generateTrees(int n) {
vector<TreeNode*>ans;
if (n <= 0)return ans;
return generateTrees(1, n);
}
};
力扣 99. 恢复二叉搜索树
给你二叉搜索树的根节点 root
,该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下,恢复这棵树 。
示例 1:
输入:root = [1,3,null,null,2] 输出:[3,1,null,null,2] 解释:3 不能是 1 的左孩子,因为 3 > 1 。交换 1 和 3 使二叉搜索树有效。
示例 2:
输入:root = [3,1,4,null,null,2] 输出:[2,1,4,null,null,3] 解释:2 不能在 3 的右子树中,因为 2 < 3 。交换 2 和 3 使二叉搜索树有效。
提示:
- 树上节点的数目在范围
[2, 1000]
内 -231 <= Node.val <= 231 - 1
进阶:使用 O(n)
空间复杂度的解法很容易实现。你能想出一个只使用 O(1)
空间的解决方案吗?
思路一:常规遍历
class Solution {
public:
void recoverTree(TreeNode* root) {
auto v = InorderTraversal(root);
int id1 = -1, id2;
for (int i = 1; i < v.size(); i++) {
if (v[i - 1] > v[i]) {
if(id1==-1)id1 = i-1;
id2 = i;
}
}
recoverTree(root, v[id1], v[id2]);
}
void recoverTree(TreeNode* root,int x1,int x2) {
if (root->val == x1)root->val = x2;
else if (root->val == x2)root->val = x1;
if (root->left)recoverTree(root->left, x1, x2);
if (root->right)recoverTree(root->right, x1, x2);
}
};
时间复杂度O(n)
空间复杂度O(n)
思路二:Morris遍历
时间复杂度O(n)
空间复杂度O(1)
力扣 270. 最接近的二叉搜索树值
给你二叉搜索树的根节点 root
和一个目标值 target
,请在该二叉搜索树中找到最接近目标值 target
的数值。如果有多个答案,返回最小的那个。
示例 1:
输入:root = [4,2,5,1,3], target = 3.714286 输出:4
示例 2:
输入:root = [1], target = 4.428571 输出:1
提示:
- 树中节点的数目在范围
[1, 104]
内 0 <= Node.val <= 109
-109 <= target <= 109
class Solution {
public:
int closestValue(TreeNode* root, double target) {
high(root, target);
low(root, target);
if(highAns == 12345678900)return lowAns;
if(lowAns == -12345678900)return highAns;
if(highAns-target<target-lowAns)return highAns;
return lowAns;
}
void high(TreeNode* root, double target){
if(!root)return;
if(root->val>=target){
highAns=min(highAns,(long long)root->val);
high(root->left,target);
}else{
high(root->right,target);
}
}
void low(TreeNode* root, double target){
if(!root)return;
if(root->val<=target){
lowAns=max(lowAns,(long long)root->val);
low(root->right,target);
}else{
low(root->left,target);
}
}
long long highAns = 12345678900;
long long lowAns = -12345678900;
};
力扣 776. 拆分二叉搜索树
给你一棵二叉搜索树(BST)的根结点 root 和一个整数 target 。请将该树按要求拆分为两个子树:其中一个子树结点的值都必须小于等于给定的目标值;另一个子树结点的值都必须大于目标值;树中并非一定要存在值为 target 的结点。
除此之外,树中大部分结构都需要保留,也就是说原始树中父节点 p 的任意子节点 c ,假如拆分后它们仍在同一个子树中,那么结点 p 应仍为 c 的父结点。
返回 两个子树的根结点的数组 。
示例 1:
输入:root = [4,2,6,1,3,5,7], target = 2
输出:[[2,1],[4,3,6,null,null,5,7]]
示例 2:
输入: root = [1], target = 1
输出: [[1],[]]
提示:
二叉搜索树节点个数在 [1, 50] 范围内
0 <= Node.val, target <= 1000
class Solution {
public:
vector<TreeNode*> splitBST(TreeNode* root, int target) {
vector<TreeNode*>ans;
if (!root) {
ans.resize(2);
ans[0] = ans[1] = NULL;
return ans;
}
if (root->val > target) {
ans = splitBST(root->left, target);
root->left = ans[1];
ans[1] = root;
}
else {
ans = splitBST(root->right, target);
root->right = ans[0];
ans[0] = root;
}
return ans;
}
};
力扣 897. 递增顺序搜索树
给你一棵二叉搜索树的 root
,请你 按中序遍历 将其重新排列为一棵递增顺序搜索树,使树中最左边的节点成为树的根节点,并且每个节点没有左子节点,只有一个右子节点。
示例 1:
输入:root = [5,3,6,2,4,null,8,1,null,null,null,7,9] 输出:[1,null,2,null,3,null,4,null,5,null,6,null,7,null,8,null,9]
示例 2:
输入:root = [5,1,7] 输出:[1,null,5,null,7]
提示:
- 树中节点数的取值范围是
[1, 100]
0 <= Node.val <= 1000
class Solution {
public:
TreeNode* increasingBST(TreeNode* root) {
auto v=inorderTraversal(root);
TreeNode* r = new TreeNode(v[0]);
auto p=r;
for(int i=1;i<v.size();i++){
p->right=new TreeNode(v[i]);
p=p->right;
}
return r;
}
};
力扣 938. 二叉搜索树的范围和
给定二叉搜索树的根结点 root
,返回值位于范围 [low, high]
之间的所有结点的值的和。
示例 1:
输入:root = [10,5,15,3,7,null,18], low = 7, high = 15 输出:32
示例 2:
输入:root = [10,5,15,3,7,13,18,1,null,6], low = 6, high = 10 输出:23
提示:
- 树中节点数目在范围
[1, 2 * 104]
内 1 <= Node.val <= 105
1 <= low <= high <= 105
- 所有
Node.val
互不相同
class Solution {
public:
int rangeSumBST(TreeNode* root, int low, int high) {
auto v= InorderTraversal(root);
int s = 0;
for (auto x : v)if (x >= low && x <= high)s += x;
return s;
}
};
力扣 1214. 查找两棵二叉搜索树之和
给出两棵二叉搜索树的根节点 root1 和 root2 ,请你从两棵树中各找出一个节点,使得这两个节点的值之和等于目标值 Target。
如果可以找到返回 True,否则返回 False。
示例 1:
输入:root1 = [2,1,4], root2 = [1,0,3], target = 5
输出:true
解释:2 加 3 和为 5 。
示例 2:
输入:root1 = [0,-10,10], root2 = [5,1,7,0,2], target = 18
输出:false
提示:
每棵树上节点数在 [1, 5000] 范围内。
-109 <= Node.val, target <= 109
TreeNode* find(TreeNode* root, int val)
{
if (!root)return NULL;
if (root->val == val)return root;
if (root->val > val)return find(root->left, val);
return find(root->right, val);
}
class Solution {
public:
bool twoSumBSTs(TreeNode* root1, TreeNode* root2, int target) {
if (!root1)return false;
if (find(root2, target - root1->val))return true;
return twoSumBSTs(root1->left, root2, target) || twoSumBSTs(root1->right, root2, target);
}
};
力扣 2476. 二叉搜索树最近节点查询
给你一个 二叉搜索树 的根节点 root
,和一个由正整数组成、长度为 n
的数组 queries
。
请你找出一个长度为 n
的 二维 答案数组 answer
,其中 answer[i] = [mini, maxi]
:
mini
是树中小于等于queries[i]
的 最大值 。如果不存在这样的值,则使用-1
代替。maxi
是树中大于等于queries[i]
的 最小值 。如果不存在这样的值,则使用-1
代替。
返回数组 answer
。
示例 1 :
输入:root = [6,2,13,1,4,9,15,null,null,null,null,null,null,14], queries = [2,5,16] 输出:[[2,2],[4,6],[15,-1]] 解释:按下面的描述找出并返回查询的答案: - 树中小于等于 2 的最大值是 2 ,且大于等于 2 的最小值也是 2 。所以第一个查询的答案是 [2,2] 。 - 树中小于等于 5 的最大值是 4 ,且大于等于 5 的最小值是 6 。所以第二个查询的答案是 [4,6] 。 - 树中小于等于 16 的最大值是 15 ,且大于等于 16 的最小值不存在。所以第三个查询的答案是 [15,-1] 。
示例 2 :
输入:root = [4,null,9], queries = [3] 输出:[[-1,4]] 解释:树中不存在小于等于 3 的最大值,且大于等于 3 的最小值是 4 。所以查询的答案是 [-1,4] 。
提示:
- 树中节点的数目在范围
[2, 105]
内 1 <= Node.val <= 106
n == queries.length
1 <= n <= 105
1 <= queries[i] <= 106
class Solution {
public:
vector<vector<int>> closestNodes(TreeNode* root, vector<int>& queries) {
auto v = InorderTraversal(root);
vector<vector<int>>ans;
for(auto x:queries){
auto it=lower_bound(v.begin(),v.end(),x);
int high=it==v.end()?-1:*it;
int low=(it!=v.end()&&*it==x)?x:(it==v.begin()?-1:*(it-1));
ans.push_back(vector<int>{low,high});
}
return ans;
}
};
剑指 Offer 33. 二叉搜索树的后序遍历序列
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。
参考以下这颗二叉搜索树:
5
/ \
2 6
/ \
1 3
示例 1:
输入: [1,6,3,2,5]
输出: false
示例 2:
输入: [1,3,2,6,5]
输出: true
提示:
数组长度 <= 1000
class Solution {
public:
bool verifyPostorder(vector<int>& postorder,int x,int y) {
if(x>=y)return true;
int k=postorder[y];
int loc=x;
while(loc<y && postorder[loc]<k)loc++;
for(int i=loc;i<y;i++)if(postorder[i]<k)return false;
return verifyPostorder(postorder,x,loc-1) && verifyPostorder(postorder,loc,y-1);
}
bool verifyPostorder(vector<int>& postorder) {
if(postorder.size()==0)return true;
return verifyPostorder(postorder,0,postorder.size()-1);
}
};
剑指 Offer II 054. 所有大于等于节点的值之和
给定一个二叉搜索树,请将它的每个节点的值替换成树中大于或者等于该节点值的所有节点值之和。
提醒一下,二叉搜索树满足下列约束条件:
- 节点的左子树仅包含键 小于 节点键的节点。
- 节点的右子树仅包含键 大于 节点键的节点。
- 左右子树也必须是二叉搜索树。
示例 1:
输入:root = [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
和104
之间。 - 每个节点的值介于
-104
和104
之间。 - 树中的所有值 互不相同 。
- 给定的树为二叉搜索树。
思路:
因为猜到不限制是否修改原树,所以偷了个懒,否则的话调用一下二叉树copy的接口即可。
class Solution {
public:
void dfs(TreeNode* root)
{
if (!root)return;
dfs(root->right);
valSum += root->val;
root->val = valSum;
dfs(root->left);
}
TreeNode* convertBST(TreeNode* root) {
dfs(root);
return root;
}
private:
int valSum;
};
剑指 Offer 04. 二维数组中的查找
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
限制:
0 <= n <= 1000
0 <= m <= 1000
思路一,逐行二分法,时间复杂度O(n log m)
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
for(int i=0;i<matrix.size();i++)
{
if(lower_bound(matrix[i].begin(),matrix[i].end(),target)!=upper_bound(matrix[i].begin(),matrix[i].end(),target))
return true;
}
return false;
}
};
思路二,从右上角开始搜索,整个矩阵就是一颗深度为n+m的二叉搜索树
时间复杂度O(n+m)
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
if (matrix.empty()|| matrix[0].empty())return false;
int r = 0, c = matrix[0].size() - 1;
while (true) {
if (r >= matrix.size() || c < 0)return false;
if (matrix[r][c] == target)return true;
if (matrix[r][c] > target)c--;
else r++;
}
return false;
}
};
力扣 面试题 17.12. BiNode
二叉树数据结构TreeNode
可用来表示单向链表(其中left
置空,right
为下一个链表节点)。实现一个方法,把二叉搜索树转换为单向链表,要求依然符合二叉搜索树的性质,转换操作应是原址的,也就是在原始的二叉搜索树上直接修改。
返回转换后的单向链表的头节点。
注意:本题相对原题稍作改动
示例:
输入: [4,2,5,1,3,null,6,0] 输出: [0,null,1,null,2,null,3,null,4,null,5,null,6]
提示:
- 节点数量不会超过 100000。
class Solution {
public:
TreeNode* convertBiNode(TreeNode* root) {
v.clear();
dfs(root);
v.push_back(nullptr);
for(int i=0;i<v.size()-1;i++){
v[i]->left=nullptr;
v[i]->right=v[i+1];
}
return v[0];
}
void dfs(TreeNode* root){
if(!root)return;
dfs(root->left);
v.push_back(root);
dfs(root->right);
}
vector<TreeNode*>v;
};