文章目录
98.验证二叉搜索树
思路:采用自顶向下的搜索方法,如果每个元素都合理处在自己的范围里,那么是BST
关键在于每次搜索后更新当前节点(root[min,max])儿子节点的范围
左儿子更新成[min,val-1],右儿子更新成[val+1,max]
/**
* 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:
bool isValidBST(TreeNode* root) {
return dfs(root,INT_MIN,INT_MAX); //根节点范围是int上下界
}
bool dfs(TreeNode* root,long long min,long long max){
if(!root)return true; //根节点(或者子树根)为空,返回true
if(root->val>max||root->val<min)return false; //不在范围内,返回false
return dfs(root->left,min,root->val-1ll)&&dfs(root->right,root->val+1ll,max); //搜索当前节点的左右子树
}
};
94.二叉树的中序遍历 **
这题不给用递归所以得自己开一个栈迭代
思路:
1.首先把要压入树最左边的一条链压入栈
2.每次取出栈顶元素,如果有,将其右子树压入栈
3.重复操作1
/**
* 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:
vector<int> inorderTraversal(TreeNode* root) {
vector<int>res;
stack<TreeNode*> stk;
auto p=root;
while(p||stk.size()){ //只要p有指向或者栈中有元素,循环执行
while(p){
stk.push(p); //步骤一将要加入栈的树的最左链入栈
p=p->left;
}
p=stk.top();
stk.pop();
res.push_back(p->val); //步骤二取栈顶元素,将其右子树入栈(如果没有,下次循环会跳过)
p=p->right;
}
return res;
}
};
附上递归的写法
/**
* 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:
vector<int> inorderTraversal(TreeNode* root) {
vector<int>res;
return dfs(root,res);
}
vector<int> dfs(TreeNode* root,vector<int> &res){ //注意这里别忘了写引用&符号
if(!root)return res;
dfs(root->left,res);
res.push_back(root->val);
dfs(root->right,res);
return res;
}
};
附上通用解法,之前看交大数据结构也是那么教的,一直不会实现hh,前中后改个顺序即可
可以用栈来模拟这个过程。栈中每个元素存储两个值:TreeNode节点和一个整型的标记。
- 标记 = 0,表示还没遍历该节点的左子树;
- 标记 = 1,表示已经遍历完左子树,但还没遍历右子树;
- 标记 = 2,表示已经遍历完右子树;
然后我们可以根据标记的值,来分别处理各种情况:
- 标记 = 0,则将该节点的标记改成1,然后将其左儿子压入栈中;
- 标记 = 1,则说明左子树已经遍历完,将根节点的值插入答案序列中,然后将该节点的标记改成2,并将右儿子压入栈中;
- 标记 = 2,则说明以该节点为根的子树已经遍历完,直接从栈中弹出即可;
时间复杂度分析:树中每个节点仅会遍历一遍,且进栈出栈一次,所以时间复杂度是 O(n)O(n)。
/**
* 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:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<pair<TreeNode*, int>>sta;
sta.push(make_pair(root, 0));
while (!sta.empty())
{
if (sta.top().first == NULL)
{
sta.pop();
continue;
}
int t = sta.top().second;
if (t == 0)
{
sta.top().second = 1;
sta.push(make_pair(sta.top().first->left, 0));
}
else if (t == 1)
{
res.push_back(sta.top().first->val);
sta.top().second = 2;
sta.push(make_pair(sta.top().first->right, 0));
}
else sta.pop();
}
return res;
}
};
101.对称二叉树 **
思路:
递归:
以根节点为轴对称分为左右两棵树
这两棵树首先根节点值相等,其次他们的左右子树分别对称相等
递归定义即可
//递归写法
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if(!root)return true; //空节点为对称二叉树
return dfs(root->left,root->right);
}
bool dfs(TreeNode* p,TreeNode* q){
if(!p||!q)return !p&&!q; //只有左右两根节点都不存在时才对称,返回true,一个存在一个不存在,返回false
return p->val==q->val&&dfs(p->left,q->right)&&dfs(p->right,q->left);
} //根节点值要相等,左根的左树要对称右根的右树,左根的右树要对称右根的左树
};
思路
迭代
与上题相似的方法遍历左右子树,只不过左子树用左中右方法遍历,右子树用右中左方法遍历,每次从栈中取出一对称两点比较,比较完毕就是对称二叉树
//迭代写法
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if(!root)return true;
stack<TreeNode*>left,right; //分别保存左右子树遍历的值
auto p=root->left; //pq指向左右根节点
auto q=root->right;
while(p||q||left.size()||right.size()){
while(p&&q){ //pq左右链入栈
left.push(p),right.push(q);
p=p->left,q=q->right;
}
if(p||q)return false; //精髓,如果pq有一个不存在,则不对称,上面循环保证不可能两个都存在
p=left.top(),q=right.top();
left.pop(),right.pop();
if(p->val!=q->val)return false; //对称节点值不相等不存在
p=p->right,q=q->left; //左子树遍历右节点,右子树遍历左节点(对称遍历)
}
return true;
}
};
从前序和中序遍历构造二叉树
数据结构基础题的算法实现。。
思路:
前序遍历第一个节点就是根节点,在中序遍历中找到他的位置,以他为界分出左右两棵子树,每次建立一个节点,递归建立其左右子树
关键在于找前序遍历的根节点在中序的位置,如果对每一次建立节点都循环遍历一次inorder,时间复杂度会是O(n 2)
为此建立一个哈希表,存放inorder中对应数据的位置,将遍历的时间复杂度降为O(1)
class Solution {
public:
unordered_map<int,int> pos;
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n=preorder.size();
if(!n)return NULL;
for(int i=0;i<n;i++)pos[inorder[i]]=i; //初始化哈希表
return dfs(preorder,inorder,0,n-1,0,n-1);
}
TreeNode* dfs(vector<int> &preorder,vector<int> &inorder,int lp,int rp,int li,int ri){
if(lp>rp)return NULL;
int val=preorder[lp]; //当前建立的根节点的值
int k=pos[val]; //找到该节点在inorder中对应的位置
int len=k-li; //确定当前根节点左子树的元素个数
auto root=new TreeNode(val);
root->left=dfs(preorder,inorder,lp+1,lp+len,li,k-1); //递归建立左右节点
root->right=dfs(preorder,inorder,lp+len+1,rp,k+1,ri);
return root;
}
};
102.二叉树的层次遍历
思路:
常规宽搜思路,只不过每搜一层要返回一层元素,拓展之前先记录一下长度
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>>res; //记录答案
if(!root)return res;
queue<TreeNode*>q; //宽搜队列
q.push(root);
while(q.size()){
int len =q.size(); //记录当前层的长度
vector<int>level; //记录当前层的元素
for(int i=0;i<len;i++){ //拓展当前层的每一个元素
auto p=q.front();
q.pop();
if(p->left)q.push(p->left);
if(p->right)q.push(p->right);
level.push_back(p->val);
}
res.push_back(level); //返回当前层的元素
}
return res;
}
};
236.二叉树的最近公共祖先 **
思路:
拿到这题,先分析因该是个递归判断
- 首先分析当前根节点是不是祖先节点,如果他不存在返回root(leetcode特色)
由于pq不重合且肯定存在,所以如果根节点等于p或者q,也直接返回root- 其次判断左右子节点是不是祖先节点,调用当前方法返回左右节点对应的值
- 如果左右都返回了一个node,说明左右子树分别存在着pq,此时返回根节点
- 如果只有左子树返回一个node,而右子树返回NULL,说明pq都存在左子树当中,此时返回左子树node
- 如果只有右子树返回一个node,而左子树返回NULL,说明pq都存在右子树当中,此时返回右子树node
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(!root||root==p||root==q)return root; //当前节点包含p或者q,或者为空都返回这个node
auto left=lowestCommonAncestor(root->left,p,q); //分别看一下左右节点
auto right=lowestCommonAncestor(root->right,p,q);
if(!left)return right; //如果left为空返回right,right空返回left,都为空就返回NULL
if(!right)return left;
return root; //如果左右子节点都不是空就返回根节点
}
};
543.二叉树的直径 **
(自底向上的递归想法)
这题一开始完全想错了,因为有可能最长路径不从根节点走。。
- 所以枚举每一条路径的顶点(其实就是所有的点),递归求出其左右路径长度,加起来算出总长度,然后更新答案即可
- 注意这题有点像自底向上递归的感觉,,从叶子节点开始考虑,每次更新完最大值之后需要返回该节点左右最长的长度加一(左右最长代表以该节点为顶点的树的深度,加一是因为返回到上一个节点有一条边 )
class Solution {
public:
int ans=0; //最后答案
int diameterOfBinaryTree(TreeNode* root) {
if(!root)return 0;
dfs(root);
return ans;
}
int dfs(TreeNode* root){
if(!root)return 0;
int left=dfs(root->left); //每次先查看该节点左右路径长度
int right=dfs(root->right);
ans=max(ans,right+left); //left+right是以该节点为根(顶点)的最大路径,更新最大长度
return max(right+1,left+1); //返回到上一个根节点,用的是该树最大深度加一
}
};
124 .二叉树中的最大路径和
这道题做法和上一题一样
只不过在返回时多考虑几种情况
因为其不是边权和而是节点权和且存在负数节点
所以返回上一级时,最小应当返回0(返回0即表示最大路径不从这里走,只有负权路径)
class Solution {
public:
int res=INT_MIN; //初值应定为最小值
int maxPathSum(TreeNode* root) {
dfs(root);
return res;
}
int dfs(TreeNode* root){
if(!root)return 0;
int left=dfs(root->left); //搜索左右路径最大值,最小是0
int right=dfs(root->right);
res=max(res,root->val+right+left);
return max(0,root->val+(0,max(right,left))); //返回从当前节点经过路径的最大值即 从0,root->val,root->val+left,root->val+right选择一个最大值
}
};
173.二叉搜索树迭代器
这道题要求实现一个中序遍历的迭代器
我们把前面中序遍历迭代写法那题(## 94.二叉树的中序遍历)拆开写
分部分实现二叉树的函数
class BSTIterator {
public:
stack<TreeNode*>stk;
BSTIterator(TreeNode* root) { //初始化时将最左链入栈
while(root){
stk.push(root);
root=root->left;
}
}
/** @return the next smallest number */
int next() { //栈顶元素即当前最小值,每次返回后要将栈 顶元素的右子树入栈
auto p=stk.top();
stk.pop();
int val=p->val;
p=p->right;
while(p){
stk.push(p);
p=p->left;
}
return val;
}
/** @return whether we have a next smallest number */
bool hasNext() {
return !stk.empty(); //栈空则遍历结束,没有最小值
}
};
297.二叉树的序列化和反序列化 ** ??
题意:树转成字符串,字符串再转回去(都用递归方式)
用前序遍历的方法返回遍历顺序,没有的节点用#代替,保证转换回树时有唯一性
- 转字符串
- 按照根左右的顺序遍历,遇到数转字符加入字符串,遇到空节点则加入"#“作为标记,每个字符之间都要用”,"隔开
- 转二叉树
- 用下标标记遍历的位置
- 按照根左右的方式递归建立树,每次遇到",“则停止,遇到”#"则返回NULL,遇到字符就转化成数字并初始化一个节点
```cpp
class Codec {
public:
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
string res;
dfs1(root,res);
return res;
}
void dfs1(TreeNode* root,string& res){
if(!root){
res+="#,"; //注意别写成res+='#'+',';这样'#'+','会重新计算ASCLL码值并且被当成一个字符
return;
}
res+=to_string(root->val)+','; //val转化成字符并且+','
dfs1(root->left,res); //递归遍历左子树和右子树
dfs1(root->right,res);
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
int u=0; //遍历下标标记
return dfs2(data,u);
}
TreeNode* dfs2(string &data,int &u){
if(data[u]=='#'){
u+=2;
return NULL; //遇到'#'返回NULL并且下标+2
}
bool flag=false; //标记负数
int t=0;
while(data[u]!=','){
if(data[u]=='-')flag=true;
else t=t*10+data[u]-'0'; //转化成数字 字符转int:'1'-'0'=1;int转字符:1+'0'='1';
u++;
}
u++;
if(flag)t=-t; //如果为负数,转化成负数
auto root=new TreeNode(t);
root->left=dfs2(data,u); //递归建立左右子树
root->right=dfs2(data,u);
return root;
}
};
// Your Codec object will be instantiated and called as such:
// Codec codec;
// codec.deserialize(codec.serialize(root));