106. 从中序与后序遍历序列构造二叉树
题目
题目思路:【随想录】
搞清楚人做的时候的逻辑:
- 在后序数组中找到最后一个当作根节点
- 利用这个根节点到中序数组中找到该点,作为分割点,将中序数组分割成左右两个
- 利用中序数组的左右数组,再到后续中继续进行分割
难点:
每次递归的左右区间边界点要弄清楚,即循环不变量,这里用的左闭右开区间
。
**
以下是看了思路后写的,不太对!少了很多东西主要问题是划分边界值,以及少了对特殊节点的判断(空树),大逻辑正确,代码里的注释是错误点反思。
class Solution {
public:
TreeNode* buid(vector<int>&inorder,vector<int>& postorder,int inbegin,int inend,postbegin,postend)
{
TreeNode *root=(TreeNode *)malloc(sizeof(TreeNode));
root->val=postorder[postend];
if(postbegin==postend) //错误!!这两个相等那就是一个空的数组了,应该是值相差一才是一个节点
{
return p;
}
for(int i=inbegin;i<inend;i++)
{
if(inorder[i]==postorder[postend])
break;
}
//错误!!!i不是中序数组中前面部分的大小,因为是从inbegin开始的,所以中序前部分大小应该是i-inbegin(左闭右开)
//错误点二:逻辑混乱!左闭右开!!坚持着一个原则去做边界点
TreeNode* left=buid(inorder,postorder,inbegin,i,postbegin,postbegin+i);
TreeNode*right=buid(inorder,postorder,i+1,inend,postbein+i+1,postend-1);
root->left=left;
root->right=right;
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
}
};
随想录
使用数组来递归
class Solution {
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(postorder.size()==0||inorder.size()==0) return NULL;
TreeNode* root=new TreeNode(postorder[postorder.size()-1]);
if(postorder.size()==1)
{
return root;
}
int delimiter;
for(int i=0;i<inorder.size();i++)
{
if(inorder[i]==postorder[postorder.size()-1])
{
delimiter=i;
break;
}
}
vector<int> inleft(inorder.begin(),inorder.begin()+delimiter);
vector<int> inright(inorder.begin()+delimiter+1,inorder.end());//要加一,去掉分割点,分割点已经是root了
postorder.resize(postorder.size()-1);//去掉最后一个元素
//这里用中序的前半部分数组大小进行分割后续,深刻的体现了前序数组大小和后续数组大小相等的特点,如果使用delimiter作为数组大小则体现的不明显
vector<int> postleft(postorder.begin(),postorder.begin()+inleft.size());
vector<int> postright(postorder.begin()+inleft.size(),postorder.end());//后续不需要加一,因为它直接去掉了最后一个元素,也可以是下面的这种,delimiter实际上就是中序前半部分数组大小
// vector<int> postleft(postorder.begin(),postorder.begin()+delimiter);
// vector<int> postright(postorder.begin()+delimiter,postorder.end());
root->left=buildTree(inleft,postleft);
root->right=buildTree(inright,postright);
return root;
}
};
优化:
随想录
注意:数组长度是delimiter-inbegin,不是delimiter,因为是在原数组里的。
前序:6 3 2 1 5 0
中序:3 2 1 6 0 5
后序:1 2 3 0 5 6
class Solution {
private:
// 中序区间:[inorderBegin, inorderEnd),后序区间[postorderBegin, postorderEnd)
TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& postorder, int postorderBegin, int postorderEnd) {
if (postorderBegin == postorderEnd) return NULL;
int rootValue = postorder[postorderEnd - 1];
TreeNode* root = new TreeNode(rootValue);
if (postorderEnd - postorderBegin == 1) return root;
int delimiterIndex;
for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
if (inorder[delimiterIndex] == rootValue) break;
}
// 切割中序数组
// 左中序区间,左闭右开[leftInorderBegin, leftInorderEnd)
int leftInorderBegin = inorderBegin;
int leftInorderEnd = delimiterIndex;
// 右中序区间,左闭右开[rightInorderBegin, rightInorderEnd)
int rightInorderBegin = delimiterIndex + 1;
int rightInorderEnd = inorderEnd;
// 切割后序数组
// 左后序区间,左闭右开[leftPostorderBegin, leftPostorderEnd)
int leftPostorderBegin = postorderBegin;
int leftPostorderEnd = postorderBegin + delimiterIndex - inorderBegin; // 终止位置是 需要加上 中序区间的大小size
// 右后序区间,左闭右开[rightPostorderBegin, rightPostorderEnd)
int rightPostorderBegin = postorderBegin + (delimiterIndex - inorderBegin);
int rightPostorderEnd = postorderEnd - 1; // 排除最后一个元素,已经作为节点了
root->left = traversal(inorder, leftInorderBegin, leftInorderEnd, postorder, leftPostorderBegin, leftPostorderEnd);
root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, postorder, rightPostorderBegin, rightPostorderEnd);
return root;
}
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if (inorder.size() == 0 || postorder.size() == 0) return NULL;
// 左闭右开的原则
return traversal(inorder, 0, inorder.size(), postorder, 0, postorder.size());
}
};
简化:
class Solution {
public:
TreeNode* traversal(vector<int>& inorder, vector<int>& postorder,int inbegin,int inend,int postbegin,int postend) {
// if(postorder.size()==0||inorder.size()==0) return NULL;
if(postbegin==postend) return NULL;
TreeNode* root=new TreeNode(postorder[postend-1]);
if((postend-postbegin)==1)
{
return root;
}
int delimiter;
for(int i=inbegin;i<inend;i++)
{
if(inorder[i]==postorder[postend-1])
{
delimiter=i;
break;
}
}
root->left=traversal(inorder,postorder,inbegin,delimiter,postbegin,postbegin+delimiter-inbegin);
root->right=traversal(inorder,postorder,delimiter+1,inend,postbegin+delimiter-inbegin,postend-1);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(postorder.size()==0||inorder.size()==0) return NULL;
return traversal(inorder,postorder,0,inorder.size(),0,postorder.size());
}
};
方法二:迭代法 待学。。。
官方题解
105. 从前序与中序遍历序列构造二叉树
题目
方法一:递归法
class Solution {
public:
TreeNode* traversal(vector<int> preorder,vector<int> inorder ,int inbegin,int inend,int prebegin,int preend)
{
if(prebegin==preend) return NULL;
int rootval=preorder[prebegin];
TreeNode * root=new TreeNode(rootval);
if(prebegin-preend==1)
return root;
int delimiter;
for( delimiter=inbegin;delimiter<inend;delimiter++)
if(inorder[delimiter]==rootval)
break;
root->left=traversal(preorder,inorder,inbegin,delimiter,prebegin+1,prebegin+1+delimiter-inbegin);
root->right=traversal(preorder,inorder,delimiter+1,inend,prebegin+1+delimiter-inbegin,preend);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.size()==0||preorder.size()==0) return NULL;
return traversal(preorder,inorder,0,inorder.size(),0,preorder.size());
}
};
方法二:迭代 待学。。。。
官方题解
654. 最大二叉树
题目
和上面的思路一样,这里是允许空指针进递归的,所以用了if条件判断,而随想录中的使用vector来做的方法,递归终止条件是叶子节点,所以在构造的时候要判断左右vecto是否有结点,如果一个节点也没有就不构建。
【随想录】
随想录两种方法
/**
* 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* Traversal(vector<int> nums,int begin,int end)
{
if(begin==end) return NULL;
int max=INT_MIN;
for(int i=begin;i<end;i++)
{
if(nums[i]>max)
max=nums[i];
}
TreeNode* root=new TreeNode(max);
if(begin-end==1) return root;
int delimiter;
for(delimiter=begin;delimiter<end;delimiter++)
{
if(nums[delimiter]==max)
break;
}
root->left=Traversal(nums,begin,delimiter);
root->right=Traversal(nums,delimiter+1,end);
return root;
}
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
if(nums.size()==0) return NULL;
return Traversal(nums,0,nums.size());
}
};
单调栈解法:官解评论
class Solution {
public:
TreeNode* constructMaximumBinaryTree(std::vector<int>& nums) {
std::stack<TreeNode*> nodes;
TreeNode* curNode = nullptr;
for(size_t i=0; i < nums.size(); ++i) {
curNode = new TreeNode(nums[i]);
while(!nodes.empty() && nodes.top()->val < curNode->val) {
TreeNode* top = nodes.top(); nodes.pop();
if(!nodes.empty() && nodes.top()->val < curNode->val)
nodes.top()->right = top;
else
curNode->left = top;
}
nodes.push(curNode);
}
// 遍历结束,此时栈中可能还是会有一些元素
while(!nodes.empty()) {
curNode = nodes.top(); nodes.pop();
if(!nodes.empty())
nodes.top()->right = curNode;
}
return curNode;
}
};
小结:
构造二叉树有三个注意的点:【随想录周总结21】
分割时候,坚持区间不变量原则,左闭右开,或者左闭又闭。
分割的时候,注意后序 或者 前序已经有一个节点作为中间节点了,不能继续使用了。
如何使用切割后的后序数组来切合中序数组?利用中序数组大小一定是和后序数组的大小相同这一特点来进行切割。
617. 合并二叉树
题目
没做出来,看的思想录。
思路复盘:
刚开始想用层序遍历,但是是用了两个队列,错误点:每次遇到单子树的时候,重新创建了一个结点,只复制了一个而没有复制真个子树。
class Solution {
public:
TreeNode* levelTraversal(TreeNode* root1,TreeNode* root2)
{
queue<TreeNode*> que1;
queue<TreeNode*> que2;
if(root1&&root2)
{
que1.push(root1);
que2.push(root2);
}
else if(root1==NULL&&root2!=NULL)
{
return root2;
}
else if(root1!=NULL&&root2==NULL)
{
return root1;
}
else return NULL;
while(!que1.empty()&&!que2.empty())
{
TreeNode* node1=que1.front();
TreeNode* node2=que2.front();
node1->val+=node2->val;
if(node1->left&&node2->left)
{
que1.push(node1->left);
que2.push(node2->left);
node1->left->val+=node2->left->val;
}
else if(node1->left==NULL&&node2->left)
{
que2.push(node2->left);
//这里就错了!!!应该是直接将node2->left赋给node,而不是新建一个节点,这样只复制了一个结点,而没有复制整个子树!
TreeNode *node=new TreeNode(node2->left->val);
node1->left=node;
}
else if(node1->left&&node2->left==NULL)
{
que1.push(node1->left);
}
if(node1->right&&node2->right)
{
que1.push(node1->right);
que2.push(node2->right);
node1->right->val+=node2->right->val;
}
else if(node1->right==NULL&&node2->right->val)
{
que2.push(node2->right);
TreeNode *node=new TreeNode(node2->right->val);
node1->right=node;
}
else if(node1->right&&node2->right==NULL)
{
que1.push(node1->right);
}
}
// if(!que1.empty())
return root1;
}
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
return levelTraversal(root1,root2);
}
};
可以对比一下官方题解:
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
if (t1 == nullptr) {
return t2;
}
if (t2 == nullptr) {
return t1;
}
auto merged = new TreeNode(t1->val + t2->val);
auto q = queue<TreeNode*>();
auto queue1 = queue<TreeNode*>();
auto queue2 = queue<TreeNode*>();
q.push(merged);
queue1.push(t1);
queue2.push(t2);
while (!queue1.empty() && !queue2.empty()) {
auto node = q.front(), node1 = queue1.front(), node2 = queue2.front();
q.pop();
queue1.pop();
queue2.pop();
auto left1 = node1->left, left2 = node2->left, right1 = node1->right, right2 = node2->right;
if (left1 != nullptr || left2 != nullptr) {
if (left1 != nullptr && left2 != nullptr) {
auto left = new TreeNode(left1->val + left2->val);
node->left = left;
q.push(left);
queue1.push(left1);
queue2.push(left2);
} else if (left1 != nullptr) {
node->left = left1;
} else if (left2 != nullptr) {
node->left = left2;
}
}
if (right1 != nullptr || right2 != nullptr) {
if (right1 != nullptr && right2 != nullptr) {
auto right = new TreeNode(right1->val + right2->val);
node->right = right;
q.push(right);
queue1.push(right1);
queue2.push(right2);
} else if (right1 != nullptr) {
node->right = right1;
} else {
node->right = right2;
}
}
}
return merged;
}
};
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/merge-two-binary-trees/solution/he-bing-er-cha-shu-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
又想了递归形式,错误点:???
class Solution {
public:
TreeNode* Traversal(TreeNode* root1,TreeNode* root2)
{
TreeNode* root=(TreeNode*)malloc(sizeof(TreeNode));
if(root1&&root2)
{
root->val=root1->val+root2->val;
}
else if(root1==NULL&&root2!=NULL)
root->val=root2->val;
else if(root1!=NULL&&root2==NULL)
root->val=root1->val;
else return NULL;
//不应该再创建节点,因为是先序递归遍历,
// root->left=(TreeNode*)malloc(sizeof(TreeNode));
// root->right=(TreeNode*)malloc(sizeof(TreeNode));
if(root1->left&&root2->left) root->left=Traversal(root1->left,root2->left);
if(root1->right&&root2->right) root->right=Traversal(root1->right,root2->right);
return root;
}
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
return Traversal(root1,root2);
}
};
方法一:递归
先序中序后序都可以,这里用的先序
class Solution {
public:
TreeNode* Traversal(TreeNode* root1,TreeNode* root2)
{
if(root1==NULL)
return root2;
else if(root2==NULL)
return root1;
if(root1&&root2)
root1->val+=root2->val;
root1->left=Traversal(root1->left,root2->left);
root1->right=Traversal(root1->right,root2->right);
return root1;
}
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
return Traversal(root1,root2);
}
};
巧妙点一:
只判断一个是否为空,直接返回另一个:
if(root1==NULL) return root2;
if(root2==NULL) return root1;
巧妙点二:
也是本题的一个核心点,当p1->left=p2->left时,是把p2->left的所有该子树分支孩子都加到了p1->left上面了(连根拔起),所以就不用递归的去再遍历p2->left,也是本题题目条件的隐藏点。
构造新树递归:
class Solution {
public:
TreeNode* Traversal(TreeNode* root1,TreeNode* root2)
{
if(root1==NULL) return root2;
if(root2==NULL) return root1;
TreeNode* root=(TreeNode*)malloc(sizeof(TreeNode));
root->val=root1->val+root2->val;
root->left=Traversal(root1->left,root2->left);
root->right=Traversal(root1->right,root2->right);
return root;
}
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
return Traversal(root1,root2);
}
};
方法二:迭代法
与101对称二叉树是一样的思路,模拟层序遍历,用一个队列同时处理两个子树,注意处理的顺序很关键,先处理左右都有子树的,再处理单子树的(见代码注释),否则进队列的就是被改造过后的树了。
class Solution {
public:
TreeNode* Traversal(TreeNode* root1,TreeNode* root2)
{
//注意!!!一个为空一个不为空时的处理!
if(root1==NULL) return root2;
if(root2==NULL) return root1;
queue<TreeNode*>que;
if(root1&&root2)
{
que.push(root1);
que.push(root2);
}
while(!que.empty())
{
TreeNode* p1=que.front();
que.pop();
TreeNode*p2=que.front();
que.pop();
//因为空指针不进队,所以这里p1,p2都不为空。
p1->val+=p2->val;
//注意!!!这里是先入队列,即先处理两者都有的情况下,先入队列,然后再判断一个有子树一个没有子树的情况,否则,本来p1->left==NULL,加上了p2->left,p1就是左右都有了,然后就符合下面的条件,加入队列的就是改变后(合并后)的树,不是自己原本的是树了
if(p2->left!=NULL&&p1->left!=NULL)
{
que.push(p1->left);
que.push(p2->left);
}
if(p1->right!=NULL&&p2->right!=NULL)
{
que.push(p1->right);
que.push(p2->right);
}
if(p1->left==NULL&&p2->left!=NULL)
{
//直接嫁接下面所有的子树即可,不用再将这个分支的子树进队列
p1->left=p2->left;
}
if(p1->right==NULL&&p2->right!=NULL)
{
p1->right=p2->right;
}
//如果是p1->left!=NULL&&p2->left==NULL;p1->right!=NULL&&p2->right==NULL;则不需要再写,因为直接就是自己了
}
return root1;
}
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
return Traversal(root1,root2);
}
};
700. 二叉搜索树中的搜索
题目
方法一:迭代法
我写的:当成普通二叉树找个结点与val同,使用的后序遍历。这里是找到某一个结点就结束,所以递归有返回值。
class Solution {
public:
TreeNode* DFS(TreeNode*root,int val)
{
if(root==NULL) return NULL;
TreeNode*left=DFS(root->left,val);
TreeNode* right=DFS(root->right,val);
if(root->val==val) return root;
if(left!=NULL)
return left ;
if(right!=NULL)
return right;
return NULL;
}
TreeNode* searchBST(TreeNode* root, int val) {
return DFS(root,val);
}
};
看了随想录的思路,根据BFS折半查找二叉树的特点来做:
左子树不为空,则左子树上的结点一定小于根结点;
右子树不为空,右子树一定小于根节点;
左右子树又是一个折半查找二叉树。
class Solution {
public:
TreeNode* DFS(TreeNode*root,int val)
{
if(root==NULL) return NULL;
// if(root->val==val) return root;
if(root->val>val)
return DFS(root->left,val);
if(root->val<val)
return DFS(root->right,val);
return root; //这里就是相等的时候!注意递归必须有一个在if外面的返回值
}
TreeNode* searchBST(TreeNode* root, int val) {
return DFS(root,val);
}
};
随想录:
逻辑更加清晰了。
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if (root == NULL || root->val == val) return root;
if (root->val > val) return searchBST(root->left, val);
if (root->val < val) return searchBST(root->right, val);
return NULL;
}
};
迭代法:
与普通二叉树的不同之处:
以下两点都取决于BFS的有序性。
- 折半查找二叉树找一个结点的路径是规划好的,不用回溯,而普通二叉树如果没找到还需要回溯。
- 一般二叉树迭代法需要栈模拟递归,而BFS不需要栈就可以实现迭代。
不用栈模拟递归遍历,就用指针遍历树,注意循环结束条件while(),以及跳出循环后的return
二叉树一定要利用其特性!这样写的代码才简单!
自己写的:虽然过了,但是有逻辑错误!!!!!!
我想的是到了叶子结点还没找到就说明是没找到了,其实并不是,不一定是叶子结点,只要到最后p为空了,说明一条路径走完了,就直接跳出了循环,return null即可,比如4 null 6 null 7,val=5时,就是走到4->6->6的左子树,停止了,而不是在叶子结点停止的!!!
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
TreeNode* p=root;
//不用栈模拟递归遍历,就用指针遍历树
while(p)
{
//错误想法!!:叶子结点说明没找到val;
//之前的错误想法:还想的是1 ,2两个if语句顺序不能掉过来,否则如果要找的是叶子结点,话没来得及判断该叶子结点是否与val同,就直接retur NULL了,其实不需要判断是不是叶子,本题的循环结束点是p为空!!!
if(p->val==val)//1
return p;
if(p->left==NULL&&p->right==NULL)//2
return NULL;
if(p->val>val)
p=p->left;
if(p->val<val)
p=p->right;
}
return NULL;
}
};
随想录
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
while (root != NULL) {
if (root->val > val) root = root->left;
else if (root->val < val) root = root->right;
else return root;
}
return NULL;
}
};
98. 验证二叉搜索树
验证一个二叉树是不是BFS,就看找个中序遍历序列是不是递增的
错误复盘:
样例找个通不过[5,4,6,null,null,3,7],想到的是从下往上递归,即和判断平衡二叉树那道一样,如果有一颗子树不是BFS,则整个都不是,从下往上判断是用后续遍历
错因:
陷入了随想录中的第一个陷阱,是错误的,因为序列没有递增,只是判断了当前节点的左右子树是否和根节点是BFS,没有判断后面的子树和它的祖先是否复合逻辑!!
错误代码:
//想到的是从下往上递归,即和判断平衡二叉树那道一样,如果有一颗子树不是BFS,则整个都不是,从下往上判断是用后续遍历
class Solution {
public:
bool isValidBST(TreeNode* root) {
if(root==NULL) return true;
if(root->left==NULL&&root->right==NULL) return true;
if(root->left&&root->right==NULL)
{
if(root->val<=root->left->val)
return false;
else return true;
}
if(root->right&&root->left==NULL)
{
if(root->val>=root->right->val)
return false;
else return true;
}
//后面的或语句不加()就不会截断,会出现空指针->val
if(root->left&&root->right)
{
if((root->val<=root->left->val)||(root->val>=root->right->val))
return false;
else
{
bool left=isValidBST(root->left);
bool right=isValidBST(root->right);
if(left&&right)
return true;
}
}
return false;
}
};
正确的思路:
中序遍历看是否是递增序列,中序递归只需要看是否比上一个结点大即可。
随想录:用一个数组记录中序遍历的所有结点,再依次看是否是递增的。
二叉搜索树中不能有重复元素
class Solution {
public:
void inorder(TreeNode* root,vector<int>&v)
{
if(root==NULL) return;
inorder(root->left,v);
v.push_back(root->val);
inorder(root->right,v);
}
bool isValidBST(TreeNode* root) {
if(root==NULL) return true;
vector<int> v;
inorder(root,v);
for(int i=0;i<v.size()-1;i++)
if(v[i]>=v[i+1])
return false;
return true;
}
};
补充:陷阱二【随想录】
随想录
方法一:BFS的中序递归+全局变量(记录最小值或者前一个结点)
样例中最小节点 可能是int的最小值,如果这样使用最小的int来比较也是不行的。
我的思路:
有bug:
想到的是从下往上递归,即和判断平衡二叉树那道一样,如果有一颗子树不是BFS,则整个都不是,从下往上判断是用后续遍历
相用和平衡二叉树一样思想:如果前一个结点是就返回值,如果不是返回INT_MIN。用一个函数,写两个功能:返回前一个结点的值,以及前一个结点的子树是否是BFS。
反思:应该使用全局变量来记录上一个pre更加方便,如果作为参数传递,就会?????
待解决!!!
错误代码:
//想到的是从下往上递归,即和判断平衡二叉树那道一样,如果有一颗子树不是BFS,则整个都不是,从下往上判断是用后续遍历
class Solution {
public:
bool inorder(TreeNode* root,TreeNode* pre)
{
if(root==NULL) return true;
bool left=inorder(root->left,pre);
if(pre!=NULL&&root->val<=pre->val)
return false;
pre=root;
bool right= inorder(root->right,pre);
return left&&right;
}
bool isValidBST(TreeNode* root) {
if(root==NULL) return true;
return inorder(root,NULL);
}
};
正确思路:
使用一个全局变量记录每一次递归时的值,到下一次递归时如果当前结点比该结点小或者等,就不是BFS。因为后台有INT_MIN样例,所以把maxnum改成了LONG_MIN
看了随想录思想写的:
class Solution {
public:
long long maxnum =LONG_MIN;
bool inorder(TreeNode* root)
{
if(root==NULL) return true;
bool left=inorder(root->left);
if(root->val<=maxnum)
return false;
else maxnum=root->val;
bool right= inorder(root->right);
return left&&right;
}
bool isValidBST(TreeNode* root) {
if(root==NULL) return true;
return inorder(root);
}
};
如果样例中有LONG_MIN思路:
则可以每次记录最坐下的结点的值来比较。(因为判断平衡二叉树时是树的深度,所以一定大于0,可以返回-1表示不是平衡的,BFS是每个结点有值,且值可以是负数,是任意数)。
注意中间的逻辑,以及pre可能是空的情况。
//想到的是从下往上递归,即和判断平衡二叉树那道一样,如果有一颗子树不是BFS,则整个都不是,从下往上判断是用后续遍历
class Solution {
public:
TreeNode* pre=NULL;//用来记录前一个结点
bool inorder(TreeNode* root)
{
if(root==NULL) return true;
bool left=inorder(root->left);
if(pre!=NULL&&root->val<=pre->val)
return false;
// else if(pre&&root->val>pre->val)少了pre是空的情况
// {
// pre=root;
// //return true;这里不应该直接返回,应该继续判断后面的
// }
pre=root;
bool right= inorder(root->right);
return left&&right;
}
bool isValidBST(TreeNode* root) {
if(root==NULL) return true;
return inorder(root);
}
};
上面有一个递归待解决的。。。。。还有最后一次提交的迭代算法也有问题!待解决。。。。。
方法二:迭代法+记录前一个结点
随想录:
中序迭代,每次将pre与前一个相比较,注意pre第一次是空的,所以要pre&&pre->val>=p->val
class Solution {
public:
bool isValidBST(TreeNode* root) {
if(root==NULL) return true;
stack<TreeNode*>st;
TreeNode* p=root;
TreeNode* pre=NULL;
while(p||!st.empty())
{
if(p)
{
st.push(p);
p=p->left;
}else{
p=st.top();
st.pop();
if(pre&&pre->val>=p->val)
return false;
pre=p;
p=p->right;
}
}
return true;
}
};
反思:
本题的迭代没有上一题直接是BFS的简单,是因为上一题前提条件就是BFS,而这一题是让你验证BFS,所以只能使用普通的二叉树贴袋的方法进行!
我的错误:
在中序的题目上试了,也是不对的,指向了空指针,在本题是结果不对
class Solution {
public:
bool isValidBST(TreeNode* root) {
if(root==NULL) return true;
stack<TreeNode*>st;
TreeNode* pre=NULL;
while(!st.empty())
{
TreeNode* p=st.top();
if§
{
p=p->left; //(1)
st.push§;
}else{
st.pop();//空指针出栈
p=st.top();
st.pop();
if(pre&&pre->val>=p->val)
return false;
pre=p;
p=p->right;
st.push§;//因为(1)处是先指向左子树,再进栈的所以这里要把右子树进栈
}
}
return true;
}
};
530. 二叉搜索树的最小绝对差
随想录:记住二叉搜索树是有序的,所以遇到绝对值,中值,最值之类的都当成一个数组来求!
如何在遍历的过程中记录前一个结点pre?可以使用全局变量
方法一:递归中序遍历+vector记录(是递增序列)
使用了中序遍历,v是递增的,所以是求两个相邻的结点的最小差值,这才是本题的本质,是看了官方答案才直到的。。虽然通过但是没有弄清本质!
bug点:
刚开始以为是最小的两个相减,人家是任意两个节点的绝对值最小!看清题意再下手!!!!!
class Solution {
public:
void inorder(TreeNode* root,vector<int>&v)
{
if(root==NULL) return;
inorder(root->left,v);
v.push_back(root->val);
inorder(root->right,v);
}
int getMinimumDifference(TreeNode* root) {
vector<int> v;
inorder(root,v);
int min=INT_MAX;
for(int i=0;i<v.size()-1;i++)
{
if(abs(v[i]-v[i+1])<min) //其实v是单调递增的,这里可以不用abs的,因为本质没有搞清楚,所以以为自己没有利用BFS的特性,当成了普通的二叉树来做的
min=abs(v[i]-v[i+1]);
}
return min;
}
};
官方答案:引用传参使用int类型存储前一个结点的val
class Solution {
public:
void dfs(TreeNode* root, int& pre, int& ans) {
if (root == nullptr) {
return;
}
dfs(root->left, pre, ans);
if (pre == -1) {
pre = root->val;
} else {
ans = min(ans, root->val - pre);
pre = root->val;
}
dfs(root->right, pre, ans);
}
int getMinimumDifference(TreeNode* root) {
int ans = INT_MAX, pre = -1;
dfs(root, pre, ans);
return ans;
}
};
随想录方法:全局变量记录前一个结点和最小值
反思:
函数传参:
- 使用全局变量,直接在函数内部使用全局变量
- 使用引用类型的在函数内部可以改变参数值
- 使用返回值 return
这里min和pre都用的全局变量
class Solution {
public:
TreeNode* pre=NULL;
int min=INT_MAX;
void inorder(TreeNode* cur)
{
if(cur==NULL) return ;
inorder(cur->left);
//每次更新min
if(pre&&cur->val-pre->val<min)
min=cur->val-pre->val;
pre=cur; //每次更新pre
inorder(cur->right);
}
int getMinimumDifference(TreeNode* root) {
inorder(root);
return min;
}
};
方法二:迭代中序遍历+记录前一个结点
class Solution {
public:
int getMinimumDifference(TreeNode* root) {
if(root==NULL) return true;
TreeNode* pre=NULL;
TreeNode* p=root;
int min=INT_MAX;
stack<TreeNode*> st;
while(p||!st.empty())
{
if(p)
{
st.push(p);
p=p->left;
}
else{
p=st.top();
st.pop();
if(pre!=NULL&&(p->val-pre->val<min))
min=p->val-pre->val;
pre=p;
p=p->right;
}
}
return min;
}
};
错误记录:
官网用的pre是int型的,我用的指针类型,为啥这里pre就不行了呢??
引用是否可以应用空指针???
左叶子那里就可以,使用的是前序遍历
class Solution {
public:
int min=INT_MAX;
void inorder(TreeNode* cur,TreeNode *&pre)
{
if(cur==NULL) return ;
inorder(cur->left,pre);
if(pre&&cur->val-pre->val<min)
min=cur->val-pre->val;
inorder(cur->right,cur);
}
int getMinimumDifference(TreeNode* root) {
TreeNode *pre=NULL;
inorder(root,pre);
return min;
}
};
//[5,4,7]
//递归:(5,null) (4,null) (null, null)return
//回退到(4,NULL)层:递归4->right(null ,4)return
//回退到5层:(5-4)<MIN
501. 二叉搜索树中的众数
题目
我的思路:
使用unordered_map,记录每一个数的频率,然后再遍历unordered_map找到最大值(众数),再遍历一次找到second等于该最大值的val.
反思:
没有用到BFS的特点,当成了普通二叉树。
class Solution {
public:
void inorder(TreeNode* root,unordered_map<int,int>&mp)
{
if(root==NULL) return ;
inorder(root->left,mp);
if(mp.count(root->val)==0)
mp.insert(pair<int,int>(root->val,0));
else mp[root->val]++;
inorder(root->right,mp);
}
vector<int> findMode(TreeNode* root) {
unordered_map<int,int> mp;
unordered_map<int,int>::iterator it;
inorder(root,mp);
vector<int> result;
int max=INT_MIN;
for(it=mp.begin();it!=mp.end();it++)
{
if(it->second>max)
max=it->second;
}
for(it=mp.begin();it!=mp.end();it++)
{
if(it->second==max)
result.push_back(it->first);
}
return result;
}
};
随想录
当成普通二叉树时,思路:
- 用map进行计算频率,用前中后遍历都可以计数。
- 将map转成vector<pair<int,int>>对second进行从大到小排序
- 第一个v[0]就是众数,再遍历剩下的,看是否有和第一个相同的。
特别注意:map不可以直接进行排序,必须先转化成vector<pair<int,int>>再使用自定义的比较函数,用sort进行排序!
class Solution {
private:
void searchBST(TreeNode* cur, unordered_map<int, int>& map) { // 前序遍历
if (cur == NULL) return ;
map[cur->val]++; // 统计元素频率
searchBST(cur->left, map);
searchBST(cur->right, map);
return ;
}
bool static cmp (const pair<int, int>& a, const pair<int, int>& b) {
return a.second > b.second;
}
public:
vector<int> findMode(TreeNode* root) {
unordered_map<int, int> map; // key:元素,value:出现频率
vector<int> result;
if (root == NULL) return result;
searchBST(root, map);
vector<pair<int, int>> vec(map.begin(), map.end());
sort(vec.begin(), vec.end(), cmp); // 给频率排个序
result.push_back(vec[0].first);
for (int i = 1; i < vec.size(); i++) {
// 取最高的放到result数组中
if (vec[i].second == vec[0].second) result.push_back(vec[i].first);
else break;
}
return result;
}
};
方法二:利用BFS的特性
1. 中序递归
随想录:
解析的很精彩就是我一步一步卡住的思路历程,随想录都解决了我的疑惑!
随想录
反思及解题思路:
1.关于类比思维:因为BFS中序遍历是一个有序数组,所以可以类比:在一个递增的数组上是如何找众数,方法是类似的。
2. 与数组相同之处:计频率的方法与数组类似,就是比较与前一个是否相同,相同了频率就加一,BFS是找与中序遍历前一个结点是否相同。
3. 与数组的不同之处:因为众数不唯一,结果是一个集合,数组是先找到众数(最大频率),再遍历一次数组找与最大频率相同的数加入到结果集合中;而BFS是需要遍历一次,每次只要与当前的maxnum相同,就加入root->val到结果集合中,但是一旦有一个比当前的maxnum大,就推翻之前找的所有结果,即清空vector,这样保证了结果集合永远是最大的集合;
启示:当有两个相关变量时,可以同时更新这两个变量
虽然结果集合和最大值都是在变化的,且最大值与结果集合相关联,可以看成是“等价关系”,那就同时更新result和maxnum,只不过再遇到更大的值时,vec的更新是result是从头开始的。
该思想方法和找最左下结点那个题目类似,那道题的本质是找最后一层的第一个节点,但是你必须遍历每一层,所以要用一个node去记录每一层的第一个结点,直到最后遍历结束,那么node就是最后一层的第一个结点,使用的也是循环不变量,不变的是:每一层都有一个记录官node,变得是每一层得node值不同。
思想类比:本题也一样的道理,不变的是maxnum和result这两个变量,变得是这两个变量里的值,你要做的就是找到【什么时候去改变这两个变量】的逻辑
随想录:
//随想录:有序数组直接依次统计,树的话就是和前面一个结点作比较
class Solution {
public:
int maxnum=INT_MIN;
TreeNode* pre=NULL;
int count=0;
vector<int>v;
void inorder(TreeNode* root)
{
if(root==NULL) return ;
inorder(root->left);
if(pre==NULL)
count=1;
else if(root->val==pre->val)
count++;
else count=1;
pre=root;
//(1)
if(count==maxnum)//要么这里时else if
{
v.push_back(root->val);
}
//(2)
if(count>maxnum)
{
maxnum=count;//这里改变了maxnum的值,所以要放在(1)的后面,否则会影响下面的并列if判断
v.clear();
v.push_back(root->val);
}
inorder(root->right);
}
vector<int> findMode(TreeNode* root) {
inorder(root);
return v;
}
};
要注意的是:maxnum刚开始可以是0,因为count是大于0的;该题也使用到了前面的pre方法(设成全局变量),不同之处是pre是空时,说明是第一个结点,此时将count=1;
2.中序迭代法
逻辑与递归同。
//随想录:有序数组直接依次统计,树的话就是和前面一个结点作比较
class Solution {
public:
vector<int> findMode(TreeNode* root) {
int maxnum=0;
int count=0;
vector<int>v;
stack<TreeNode*> st;
TreeNode*p=root;
TreeNode* pre=NULL;
while(p||!st.empty())
{
if(p)
{
st.push(p);
p=p->left;
}
else{
p=st.top();
st.pop();
if(pre==NULL)
count=1;
else if(pre->val==p->val)
count++;
else count=1;
if(count==maxnum)
v.push_back(p->val);
else if(count>maxnum)
{
maxnum=count;
v.clear();
v.push_back(p->val);
}
pre=p;
p=p->right;
}
}
return v;
}
};
236. 二叉树的最近公共祖先
题目
方法一:后续递归
学到的:(随想录)递归函数有返回值就是要遍历某一条边,但有返回值也要看如何处理返回值
看随想录解析!!随想录解析
思路:
- 大的整体框架是后序遍历。选择原因:按照人脑就是从下往上找的,看p,q在哪再往上找到它们相交的地方,即公共节点。所以从下往上用后序。
- 理清楚定义(关键)。当遍历到当前节点x时,x是公共祖先有以下两种情况:
- x左右子树中出现了p和q;
- x就是p时,在p的左右子树中出现了q;x是q时同理
3.确定递归三部曲
- 返回值和参数:
因为需要返回公共祖先,所以return是TreeNode*(如果是返回bool,那就不能直到祖先了),并且要判断左右子树中是否找到了p或者q,因此统一需求,该返回结点既要返回公共祖先或者p,q,又要判断是否是公共祖先(即是否左右子树中有p,q),因此,因为要找p,q,如果找到了就返回p,q或者公共祖先,如果没有找到就返回NULL;然后将该信息一层一层往上穿,直到最后的根节点递归结束。
- 确定终止条件:
root = =NULL ||root= =P||root= =q
- 确定单层的逻辑:
在处理左右子树的返回值前,需要有前面的终止条件也是判断条件(如果找到了就不再遍历左右子树),如果root= =NULL||root= =P||root= =q,就返回,如果root不是则继续递归其左右子树。
处理返回后的逻辑(即回溯):看左右子树是否找到了p或者q,如果找到了说明当前的结点就是要找的最小公共祖先。
因为递归是从下往上找的,所以当前就是最小的公共祖先,然后将该祖先一层一层返回。【返回过程可看随想录】
后序遍历是天然的回溯(先处理叶子,再回退到本层处理)。
class Solution {
public:
TreeNode* DFS(TreeNode* root,TreeNode*p,TreeNode*q)
{
if(root==NULL||root==p||root==q) return root;
TreeNode* left=DFS(root->left,p,q);
TreeNode* right=DFS(root->right,p,q);
if(left&&right) return root;
if(left!=NULL) return left;
if(right!=NULL) return right;
//下面这个就是left==NULL right==NULL
return NULL;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
return DFS(root,p,q);
}
};
对比:
既要遍历整个二叉树,又要有单分支的返回值的问题:
本题也是在遍历树的同时要处理两件事:找p或q,返回公共祖先的结点
就和判断是否是平衡二叉树一样,既要返回左右子树的depth,又要判断是否是平衡二叉树。那里是如果是的话返回depth深度,不是的话返回-1。
这里是找到了p或q或者p,q的公共祖先就返回该结点,如果没有找到就返回NULL。
得到的启示:
可以看出是将两者的返回值结果统一了起来,用一个NULL或者其他特殊的标记否,另一个就是返回信息(depth和找到的结点),递归的过程就是一层一层往上将信息传递的过程。是要遍历整个二叉树的。
官方题解:局部变量+传参
待学。。。。。与随想录的不一样,是使用的bool型作为返回值
class Solution {
public:
TreeNode* ans;
bool dfs(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == nullptr) return false;
bool lson = dfs(root->left, p, q);
bool rson = dfs(root->right, p, q);
if ((lson && rson) || ((root->val == p->val || root->val == q->val) && (lson || rson))) {
ans = root;
}
return lson || rson || (root->val == p->val || root->val == q->val);
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
dfs(root, p, q);
return ans;
}
};
方法二:迭代法:
王道
官方题解:哈希存储父节点
思想:
先遍历二叉树,使用哈希存储每一个结点的父亲,从p结点根据父亲节点往上回溯,使用另一个标记是否被访问的哈希,将p的父亲都标记为true;再从q根据父亲节点往上,同时根据访问的哈希找到第一个为true的就是共同的最小的祖先。这里的哈希都是用的是int
【注】bool默认是false
class Solution {
public:
unordered_map<int,TreeNode*>fat; //当前结点的父亲结点,用int类型比较便利,因为没有其它的作用,只是起到哈希映射的作用
unordered_map<int,bool>vis;//用来标记是否被访问过,bool默认是false
void dfs(TreeNode*root)
{
//相当于直接对左右子树进行操作的同时记录下来左右子树的根节点
if(root->left)
{
fat[root->left->val]=root;
dfs(root->left);
}
if(root->right)
{
fat[root->right->val]=root;
dfs(root->right);
}
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
//存储父亲结点
if(root) fat[root->val]=NULL;
dfs(root);
TreeNode* t=p;//分别从p和q向上回溯父亲结点
while(t)
{
vis[t->val]=true;
t=fat[t->val];
}
t=q;
while(t)
{
//if(!vis[t->val]) return t;
if(vis[t->val]) return t;//因为是从下往上的所以找到的就是第一个被访问过的
t=fat[t->val];
}
return NULL;
}
};
235. 二叉搜索树的最近公共祖先
题目
学到的:这里利用的是数据结构本身的特性作为解题逻辑,并且使用了一种反向思维:虽然本质是找到q,p中间的结点(即官方说的分岔点),但是是从区间两边进行判断的。
方法一:递归
随想录:
递归是【root->left,root->right】的左闭右闭
注意p,q本身是最小公共祖先的情况(else包含了)
class Solution {
public:
TreeNode* dfs(TreeNode* root,TreeNode*p,TreeNode*q)
{
if(root==NULL) return NULL;
if(root->val>p->val&&root->val>q->val)
{
TreeNode* left=dfs(root->left,p,q);
//左边找到了就return left
if(left)
return left;
}
else if(root->val<p->val&&root->val<q->val)
{
TreeNode* right=dfs(root->right,p,q);
//右边找到了就return right
if(right)
return right;
}
//找到了最小公共祖先
return root;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
return dfs(root,p,q);
}
};
反思:
递归的方法使用的是分支中 就return
官方题解:两次遍历
class Solution {
public:
vector<TreeNode*> getPath(TreeNode* root, TreeNode* target) {
vector<TreeNode*> path;
TreeNode* node = root;
while (node != target) {
path.push_back(node);
if (target->val < node->val) {
node = node->left;
}
else {
node = node->right;
}
}
path.push_back(node);
return path;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
vector<TreeNode*> path_p = getPath(root, p);
vector<TreeNode*> path_q = getPath(root, q);
TreeNode* ancestor;
for (int i = 0; i < path_p.size() && i < path_q.size(); ++i) {
if (path_p[i] == path_q[i]) {
ancestor = path_p[i];
}
else {
break;
}
}
return ancestor;
}
};
方法二:迭代法
很巧妙!!虽然是从上往下进行的,但是是可以找到第一个公共祖先的。原因是:如果else表示的就是分叉点,即值在p,q之间的,为什么是第一个呢就能保证是“最小”公共祖先呢????????
注意:
- 这几种情况是互斥的,所以用else if,最后的else 就包含了:p大q小,p小q大,或者pq本身就是最小的公共结点
- 因为题目告诉了一定会有p,q存在,所以一定有公共节点,所以循环终止就是return 或者p为空。
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
TreeNode*t=root;
while(t)
{
if(t->val>p->val&&t->val>q->val) t=t->left;
else if(t->val<p->val&&t->val<q->val) t=t->right;
else return t;
}
return NULL;
}
};
//023456789
//按照中序遍历,设p<q,则应该先遍历q,再遍历p,要找到p,q的公共祖先就是其实一定是比q大比p小或者就是p,q,
//一个结点是二叉搜索树的最近公共祖先的条件是:左子树中有p,右子树中有q(假设的是p<q),再加上后序遍历就是从下往上找,这两个条件就是本题的解题逻辑
//少考虑了p或者q本身就是最近公共祖先
//我写的这个其实是找到第一个在p,q区间的点,而不是第一个公共的祖先
二叉搜索树的特点是可以根据比较结点值直接从根沿着固定的路径找到目标结点的,该固定路径就是该节点的祖先,不用分前中后序就可以找到p的路径。
我看了官方答案写的:错误点:
class Solution {
public:
void dfs(TreeNode* root,TreeNode*p,vector<TreeNode*>&v)
{
if(root) return;
if(root->val==p->val) return;
v.push_back(root);
if(root->val>p->val)
{
dfs(root->left,p,v);
}
else if(root->val<p->val)
{
dfs(root->right,p,v);
}
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
vector<TreeNode*>v1;
vector<TreeNode*>v2;
dfs(root,p,v1);
dfs(root,q,v2);
int n=v2.size()<v1.size()?v2.size():v1.size();
int i=v1.size()-1;
int j=v2.size()-1;
while(n--)
{
if(v1[i]->val==v2[j]->val)
return v1[i];
i--;
j--;
}
return NULL;
}
};
以上两道题目都没有彻底理解,需要再次强化消化
BFT中一定要注意特点,就是BFT的子树也全是BFT,所以一个节点的左子树全部都比它小,即使后面有分支,也是在这个大前提条件下的,即相当于划分成了一半,一边比自己小,一边比自己大,然后找到中间值的结点作为根节点,继续分别划分。所以删除一个值等于val的结点,那么它的左右子树中绝对不可能再次出现该结点。
701. 二叉搜索树中的插入操作
题目
方法一:递归
父亲结点与子结点的操作可以使用有返回值的递归来操作,每次用它的父亲结点接住返回的值即可
该题目递归返回值有两种情况:一种是找到了被插入到位置,就返回被插入的结点,一种是插入完毕,返回根节点root;这两种情况都是一层一层往上传递信息的,最终将被插入节点插入好后,返回的是root
class Solution {
public:
TreeNode* traversal(TreeNode* root,int val)
{
//如果是空,则说明找到了该节点,就构造这个结点并返回到上一层,上一层就是他的父亲
if(root==NULL)
{
TreeNode *node=new TreeNode(val);
return node;
}
//返回到本层后,root->left或root->right就是被插入节点的父亲,直接赋值给它父亲即可
if(root->val>val) root->left=traversal(root->left,val);
if(root->val<val) root->right=traversal(root->right,val);
//插入完毕后直接返回该root
return root;
}
TreeNode* insertIntoBST(TreeNode* root, int val) {
return traversal(root,val);
}
};
使用全局变量记录前一个结点(父亲节点)
bug点:注意一旦插入成功就要return; 否则继续执行会有空指针
class Solution {
public:
TreeNode* pre=NULL;
void traversal(TreeNode* root,int val)
{
if(root==NULL)
{
TreeNode* node=new TreeNode(val);
if(pre->val>val) pre->left=node;
else pre->right=node;
return ;//千万别忘记!!!!否则root为空但是继续往下执行
}
pre=root;//pre写在这
if(root->val>val) traversal(root->left,val);
if(root->val<val) traversal(root->right,val);
return ;
}
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(root==NULL)
{
return new TreeNode(val);
}
traversal(root,val);
return root;
}
};
反思:可以看出带有返回值的处理方式更加便捷
方法二:迭代法
我的思路:
就是当成了一个BST(折半查找二叉树)的搜索val,因为树中没有val,所以最终p为空,当p为空时即一个分支走到头了,也就是val应该所处的位置,所以要用另一个指针记录他的父亲结点,最后插入到该父亲子树的正确位置上。
启示:什么时候用if else ,什么时候用if并列?
【这是一个易错点!】
当前一个if内会改变值时(该值下面那个if也要用),会影响到后面的if时,就算看起来时并列的也要写成else if
eg: if(p->val>val){p=p->left}
else if(p->val<val) {p=p->right}; //这里要使用else if
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
TreeNode* node=new TreeNode(0);
node->val=val;
if(root==NULL) return node;
TreeNode* p=root;
TreeNode* father=p;
while(p)
{
father=p;
if(p->val>val)
{
p=p->left;
}
else if(p->val<val)
{
p=p->right;
}
}
// if(father->left==NULL) father->left=node;
// else father->right=node;
if(father->val>val) father->left=node;
else father->right=node;
return root;
}
};
450. 删除二叉搜索树中的节点
题目
递归:随想录
new和delete的使用:
被删除的结点分五种情况:
- 空
- 叶子
- 只有左孩子
- 只有右孩子
- 左右孩子都有(随想录解析)
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if(root==NULL) return NULL;
if(root->val==key)
{
if(root->left==NULL&&root->right==NULL)
{
delete root;
return NULL;
}
if(root->left==NULL&&root->right)
{
auto temp=root->right;
delete root;
return temp;
}
if(root->left&&root->right==NULL)
{
auto temp=root->left;
delete root;
return temp;
}
if(root->left&&root->right)
{
TreeNode* p=root->right;
while(p->left)
{
p=p->left;
}
p->left=root->left;
TreeNode* temp=root;
root=root->right;
delete temp;
return root;
}
}
if(root->val>key) root->left=deleteNode(root->left,key);
if(root->val<key) root->right=deleteNode(root->right,key);
return root;
}
};
迭代方法
普通二叉树删除
108. 将有序数组转换为二叉搜索树
题目
方法一:递归
看了随想录思路写的。
本质:
与用前序加中序构建树题目类似,都是不断分割数组区间找到根节点,分别构建左右子树,这里的根节点是数组的中间结点,每次相当于是不断找数组的中间结点构建根节点,来构建的整个树
思路复盘:
平衡二叉搜索树 堆?
建立大根堆的方法?
还是说变成而二叉搜索树再修剪?不太好实现
不会做原因:
搜索二叉树的规律没有找到,与数组的联系,增删改查没有熟悉,与普通二叉树的联系,与平衡二叉树的联系
class Solution {
public:
//[)
TreeNode* build(vector<int>&nums,int begin,int end)
{
if(begin==end) return NULL;
int mid=(begin+end)/2;
TreeNode* root=new TreeNode(nums[mid]);
root->left=build(nums,begin,mid);
root->right=build(nums,mid+1,end);
return root;
}
TreeNode* sortedArrayToBST(vector<int>& nums) {
return build(nums,0,nums.size());
}
};
bug点:
递归终点不要忘记!
随想录笔记:
- 删除二叉树节点,增加二叉树节点,都是用递归函数的返回值来完成,这样是比较方便的
- 在构造二叉树的时候尽量不要重新定义左右区间数组,而是用下标来操作原数组。
- 题目中说要转换为一棵高度平衡二叉搜索树。这和转换为一棵普通二叉搜索树有什么差别呢?其实这里不用强调平衡二叉搜索树,数组构造二叉树,构成平衡树是自然而然的事情,因为大家默认都是从数组中间位置取值作为节点元素,一般不会随机取,所以想构成不平衡的二叉树是自找麻烦。
官方题解笔记:
二叉搜索树的中序遍历是升序序列,题目给定的数组是按照升序排序的有序数组,因此可以确保数组是二叉搜索树的中序遍历序列。
给定二叉搜索树的中序遍历,是否可以唯一地确定二叉搜索树?答案是否定的。如果没有要求二叉搜索树的高度平衡,则任何一个数字都可以作为二叉搜索树的根节点,因此可能的二叉搜索树有多个。
如果增加一个限制条件,即要求二叉搜索树的高度平衡,是否可以唯一地确定二叉搜索树?答案仍然是否定的
随想录:
左闭右闭区间
class Solution {
private:
TreeNode* traversal(vector<int>& nums, int left, int right) {
if (left > right) return nullptr;
int mid = left + ((right - left) / 2);
TreeNode* root = new TreeNode(nums[mid]);
root->left = traversal(nums, left, mid - 1);
root->right = traversal(nums, mid + 1, right);
return root;
}
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
TreeNode* root = traversal(nums, 0, nums.size() - 1);
return root;
}
};
方法二:迭代法
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
queue<TreeNode*> que;
queue<int>leftSt;
queue<int>rightSt;
//创造根节点
TreeNode* root=new TreeNode(0);
que.push(root);
leftSt.push(0);
rightSt.push(nums.size()-1);
while(!que.empty())
{
int left=leftSt.front(); leftSt.pop();
int right=rightSt.front(); rightSt.pop();
int mid=(left+right)/2;
// TreeNode* cur=new TreeNode(nums[mid]); 注意边界点
TreeNode* cur=que.front(); que.pop();
cur->val=nums[mid];
if(left<=mid-1)
{
//leftSt.push(0);
leftSt.push(left);
rightSt.push(mid-1);
cur->left=new TreeNode(0);
que.push(cur->left);
}
if(right>=mid+1)
{
leftSt.push(mid+1);
//rightSt.push(nums.size()-1);
rightSt.push(right);
cur->right=new TreeNode(0);
que.push(cur->right);
}
}
return root;
}
};
1382. 将二叉搜索树变平衡
题目
递增数组(BFT)构造平衡二叉树是很天然的事情,每次取中间结点当作根节点,构造出的BT不唯一
我的思路:
将BFT中序遍历变成一个vector,然后再和上题一样,变成平衡二叉树BT
class Solution {
public:
vector<int> v;
void inorder(TreeNode* root)
{
if(root==NULL) return;
inorder(root->left);
v.push_back(root->val);
inorder(root->right);
}
TreeNode* build(int begin,int end)
{
if(begin==end) return NULL;
int mid=(begin+end)/2;
TreeNode* node=new TreeNode(v[mid]);
node->left=build(begin,mid);
node->right=build(mid+1,end);
return node;
}
TreeNode* balanceBST(TreeNode* root) {
inorder(root);
return build(0,v.size());
}
};
官方题解:贪心构造,待学。。。
538. 把二叉搜索树转换为累加树
题目
我的解题逻辑:
记录当前结点的前面的值,然后用总和减去前面的值,
算法:
第一次使用中序遍历,使用全局变量记录每一个值得前面结点的和(即小于该值的和),这里注意会加上了当前结点的值,因为最后的结果是>=,所以前面的和应该是<而不是<=,所以加上后要减去
第二次为了代码复用,所以用了一个标志f, 使用中序遍历,修改当前结点的值
二叉搜索树值是唯一的所以可以使用哈希
class Solution {
public:
int presum=0;
void inorder(TreeNode* root,unordered_map<int,int>&mp,int f)
{
if(root==NULL) return;
inorder(root->left,mp,f);
if(f==0)
{
presum+=root->val;
mp[root->val]=presum-root->val;
// root->val=presum-root->val; //
}
if(f==1)
{
root->val=presum-mp[root->val]; //last time its value is all num sum
}
inorder(root->right,mp,f);
}
TreeNode* convertBST(TreeNode* root) {
unordered_map<int,int> mp;
inorder(root,mp,0);
inorder(root,mp,1);
return root;
}
};
随想录:
思路:搜索二叉树的反向中序遍历
转换思路想象成一个递增的数组,就是从后往前相加;变成树的话,从前往后:中序遍历是左中右,那从后往前就是逆着来:右中左。然后再用一个全局的变量sum累加即可或者是用一个全局的记录前一个几点,每次累加前一个节点的值。
我看的思路写的:
class Solution {
public:
int sum=0;
void inorder(TreeNode* root)
{
if(root==NULL) return ;
inorder(root->right);
sum+=root->val;
root->val=sum;
inorder(root->left);
}
TreeNode* convertBST(TreeNode* root) {
inorder(root);
return root;
}
};
随想录写的:
class Solution {
private:
int pre; // 记录前一个节点的数值
void traversal(TreeNode* cur) { // 右中左遍历
if (cur == NULL) return;
traversal(cur->right);
cur->val += pre;
pre = cur->val;
traversal(cur->left);
}
public:
TreeNode* convertBST(TreeNode* root) {
pre = 0;
traversal(root);
return root;
}
};
迭代法:
class Solution {
public:
void inorder(TreeNode* root)
{
if(root==NULL) return;
stack<TreeNode*> st;
TreeNode* p=root;
TreeNode* pre=NULL;
while(p||!st.empty())
{
if(p)
{
st.push(p);
p=p->right;
}else{
p=st.top();
st.pop();
// pre=p;放到这每次加的都是自己
if(pre)
{
p->val+=pre->val;
}
pre=p;
p=p->left;
}
}
}
TreeNode* convertBST(TreeNode* root) {
inorder(root);
return root;
}
};
随想录写的:
如果只是使用节点的值,那么记录前一个节点的值即可,使用int,不必使用指针记录前一个结点
class Solution {
private:
int pre; // 记录前一个节点的数值
void traversal(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* cur = root;
while (cur != NULL || !st.empty()) {
if (cur != NULL) {
st.push(cur);
cur = cur->right; // 右
} else {
cur = st.top(); // 中
st.pop();
cur->val += pre;
pre = cur->val;
cur = cur->left; // 左
}
}
}
public:
TreeNode* convertBST(TreeNode* root) {
pre = 0;
traversal(root);
return root;
}
总结
随想录:
- 涉及到二叉树的构造,无论普通二叉树还是二叉搜索树一定前序,都是先构造中节点。
- 求普通二叉树的属性,一般是后序,一般要通过递归函数的返回值做计算。
- 求二叉搜索树的属性,一定是中序了,要不白瞎了有序性了。