二叉树的遍历方式
二叉树三大遍历
//前序遍历迭代方法
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root)
{
//用栈模拟递归
vector<int> res;
if(!root)return res;
stack<TreeNode*> s;
s.push(root);
while(!s.empty())
{
TreeNode*temp=s.top();
s.pop();
res.push_back(temp->val);
if(temp->right)s.push(temp->right);
if(temp->left)s.push(temp->left);
}
return res;
}
};
//中序遍历迭代
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root)
{
vector<int> res;
stack<TreeNode*>s;
TreeNode*cur=root;
while(cur||!s.empty())
{
if(cur)
{
s.push(cur);
cur=cur->left;
}
else //cur为空表示此时先输出父节点再压入右子树
{
cur=s.top();
s.pop();
res.push_back(cur->val);//此三行处理父节点
cur=cur->right;//压入右子树
}
}
return res;
}
};
//后序遍历迭代
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root)
{
//用栈模拟递归
vector<int> res;
if(!root)return res;
stack<TreeNode*> s;
s.push(root);
while(!s.empty())
{
TreeNode*temp=s.top();
s.pop();
res.push_back(temp->val);
if(temp->left)s.push(temp->left);
if(temp->right)s.push(temp->right);
}
//前序:中左右 ->(调整压栈顺序)中右左 -》翻转 左右中
reverse(res.begin(), res.end());
return res;
}
};
二叉树层序遍历
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())
{
vector<int>temp;
TreeNode *p;
//first=p=q.top();
int num=q.size();
while(num--)
{
p=q.front();
temp.push_back(p->val);
if(p->left)q.push(p->left);
if(p->right)q.push(p->right);
q.pop();
}
res.push_back(temp);
}
return res;
}
};
二叉树的修改
翻转二叉树
class Solution {
public:
TreeNode* invertTree(TreeNode* root)
{//前序遍历
if(!root)return NULL;
//前序遍历递归法
swap(root->left,root->right);//父
invertTree(root->left);//左子
invertTree(root->right);//右子
return root;
}
};
//前序遍历迭代法
class Solution {
public:
TreeNode* invertTree(TreeNode* root)
{//前序遍历
if(!root)return NULL;
stack<TreeNode*>stk;
stk.push(root);
while(!stk.empty())
{
TreeNode *temp=stk.top();
stk.pop();
swap(temp->left,temp->right);
if(temp->right)stk.push(temp->right);
if(temp->left)stk.push(temp->left);
}
return root;
}
};
//层序遍历法
class Solution {
public:
TreeNode* invertTree(TreeNode* root)
{
if(!root)return NULL;
queue<TreeNode*>que;
que.push(root);
while(!que.empty())
{
int n=que.size();
for(int i=0;i<n;i++)
{
TreeNode *temp=que.front();
swap(temp->left,temp->right);
if(temp->left)que.push(temp->left);
if(temp->right)que.push(temp->right);
que.pop();
}
}
return root;
}
};
对称二叉树
//注意此题不是判断左右字树是否一样,而是判断左子树的左子树和右子树的右子树,左子树的右子树和右子树的左子树是否一样
class Solution {
public:
bool judje(TreeNode *p,TreeNode *q)
{
if(!p&&!q)return true;
if(!p||!q)return false;
if(p->val!=q->val)return false;
return judje(p->left,q->right)&&judje(p->right,q->left);
}
bool isSymmetric(TreeNode* root)
{
return judje(root->left,root->right);
}
};//递归法
class Solution {
public:
bool isSymmetric(TreeNode* root)
{
//迭代法,注意此处不是层序遍历,而是用仅仅容器来储存比较元素
queue<TreeNode*>que;
que.push(root->left);
que.push(root->right);
while(!que.empty())
{
TreeNode*left=que.front();que.pop();
TreeNode*right=que.front();que.pop();
if(!left&&!right)continue;
if(!left||!right)return false;
if(left->val!=right->val)return false;
que.push(left->left);que.push(right->right);
que.push(left->right);que.push(right->left);
}
return true;
}
};
二叉树的路径
回溯
113. 路径总和 II
class Solution {
public:
vector<vector<int>>res;
vector<int> path;
void backtring(TreeNode *root,int targetSum)
{
if(!root)return ;
if(!root->left&&!root->right)
{//到叶子结点,满足条件的路径加进去
if(targetSum==0)res.push_back(path);
return ;
}
if(root->left)
{
path.push_back(root->left->val);
backtring(root->left,targetSum-root->left->val);//带回溯
path.pop_back();
}
if(root->right)
{
path.push_back(root->right->val);
backtring(root->right,targetSum-root->right->val);
path.pop_back();
}
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum)
{
if(!root)return res;
path.push_back(root->val);
backtring(root,targetSum-root->val);
return res;
}
};
构造二叉树
根本就是通过数据集中的某元素确立根节点,再通过调整数据集,确立左右子树
/*106
已知中序和后续
第⼀步:如果数组⼤⼩为零的话,说明是空节点了。
第⼆步:如果不为空,那么取后序数组最后⼀个元素作为节点元素。
第三步:找到后序数组最后⼀个元素在中序数组的位置,作为切割点
第四步:切割中序数组,切成中序左数组和中序右数组
第五步:切割后序数组,切成后序左数组和后序右数组第六步:递归处理左区间和右区间
9,3,15,20,7
9,15,7,20,3
(1) 3 取后续遍历的最后一个元素作为当前子树的父节点
(2) 9 - 15 20 7 将中序遍历从division处分为两段
(3) 9 _15 7 20 将后序遍历分为两段(前面的去掉中序左端,后面去掉最后一个根)
(4) 9 -9 左子树
15 20 7 -15 7 20右子树
*/
class Solution {
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder)
{
if(inorder.size()==0)return NULL;//已经造完
TreeNode *root;
int root_val=postorder[postorder.size()-1];
root=new TreeNode(root_val);
//(1)取后续遍历的最后一个元素作为当前子树的父节点
if(inorder.size()==1)//只剩下一个元素构造叶子返回
{
return root;
}
int division;//记录分割位置
for(int i=0;i<inorder.size();i++)
{//找到分割位置
if(inorder[i]==root_val)
{
division=i;
break;
}
}
//(2)将中序遍历从division处分为两段
vector<int>left_inorder(inorder.begin(),inorder.begin()+division);
//left是[0,division) right是(division,end]
vector<int>right_inorder(inorder.begin()+division+1,inorder.end());
// (3)将后序遍历分为两段(前面的去掉中序左端,后面去掉最后一个根)
//[0,division) [division,end-1]
postorder.pop_back();//去掉最后一个
vector<int>left_post(postorder.begin(),postorder.begin()+division);
vector<int>right_post(postorder.begin()+division,postorder.end());
//(4)根据新的分段两种序列求其左右子树
root->left=buildTree(left_inorder,left_post);
root->right=buildTree(right_inorder,right_post);
return root;
}
};
//已知前序和中序与之类似
//654 最大二叉树
class Solution {
public:
TreeNode* constructMaximumBinaryTree(vector<int>& nums)
{
if(!nums.size())return NULL;
TreeNode *root;
int max=nums[0];//找到最大值作子树的根节点
int div=0;
for(int i=1;i<nums.size();i++)
{
if(nums[i]>max){
max=nums[i];
div=i;
}
}
root=new TreeNode(max);
if(nums.size()==1)return root;
//得到分割后的前后缀
vector<int>left_nums(nums.begin(),nums.begin()+div);
vector<int>right_nums(nums.begin()+div+1,nums.end());
//根据前后缀构造左右子树
root->left=constructMaximumBinaryTree(left_nums);
root->right=constructMaximumBinaryTree(right_nums);
return root;
}
};
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-1);
root->right=build(nums,mid+1,end);
return root;
}
TreeNode* sortedArrayToBST(vector<int>& nums)
{
return build(nums,0,nums.size()-1);
}
};
二叉搜素树
利用好二叉搜索树中序遍历有序特点(很多问题设立一个上次遍历的指针pre很好用)
查找问题迭代吗简单到哭 //迭代方法 小于往右走,大于往左走,遇到返回即可
二叉搜素树中序有序的操作
700 二叉搜索树中的搜索
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val)
{
//递归法
// if(!root)return NULL;
// if(root->val==val)return root;
// if(root->val>val)return searchBST(root->left,val);
// else return searchBST(root->right,val);
TreeNode *p=root;
//迭代法
while(p)
{//鉴于二叉搜索树的特殊性(根不行左或右一定不行)
//不需要回溯,大于往左走,小于往右走即可
if(p->val==val)return p;
else if(p->val>val)p=p->left;
else p=p->right;
}
return p;
}
};
//98 验证二叉搜索树
class Solution {
public:
/*
不能光判断一个父节点的左右子是否小于大于他(如下例)
5
/ \
3 6
/ \
2 7
所以用一个last来记录
因为二叉搜索树的中序遍历即为有序序列,可以判断中序遍历是否是有序
可以先将中序遍历读出来再判断,也可以边遍历边判断
*/ long long last;
bool isValid(TreeNode *root)
{//last记录root上次的遍历值 定义为longlong 最小为了root->val==2^31-1时不出错
if(!root)return true;
bool left=isValid(root->left);
if(root->val<=last)return false;
last=root->val;
bool right=isValid(root->right);
return left&&right;
}
bool isValidBST(TreeNode* root)
{
last=LONG_MIN;
return isValid(root);
}
};
530. 二叉搜索树的最小绝对差
#define INF 100001
class Solution {
public:
int min_val=INF,last=INF;
void getMin(TreeNode* root)
{//last记录上次遍历的值,min_val记录出现过的最小差
if(!root)return ;
getMin(root->left);
int data=abs(root->val-last);
last=root->val;
if(data<min_val)min_val=data;
getMin(root->right);
return ;
}
int getMinimumDifference(TreeNode* root)
{//结果一定在中序遍历某个节点减左或右产生
getMin(root);
return min_val;
}
};
538. 把二叉搜索树转换为累加树
/*
本题由于是搜索树,中序遍历的有序性体现出来了
其实本题 5 6 7 8 -》26 21 15 8求有序数组的后缀和(操作数组的画就是遍历)
换为二叉树 后缀则将中序遍历化为反中序遍历 右中左,得到递减序列
每到一个结点的值为前面遍历到的所有结点值的和
*/
class Solution {
public:
TreeNode *pre;//记录上次遍历的指针 root的值即为pre的值加原root的值
void concert(TreeNode*root)
{
if(!root)return ;
concert(root->right);
if(pre)root->val+=pre->val;
pre=root;
concert(root->left);
}
TreeNode* convertBST(TreeNode* root)
{
pre=NULL;
concert(root);
return root;
}
};
二叉搜素树插入
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val)
{//迭代法 利用搜素树特性指针左右’拐弯‘,找到合适结点,把val当叶子结点插入
TreeNode*node,*cur;
node=new TreeNode(val);
if(!root)return node;//空树则根据val建立一个树
cur=root;
while(cur)//cur指针左右动往下走,遇到合适结点左右子空插入其左/右子
{
if(!cur->left&&val<cur->val)
{//遇到结点且左子空,且val插入满足左子小于根,插入结束即可
cur->left=node;
break;
}
if(!cur->right&&val>cur->val)
{
cur->right=node;
break;
}
if(cur->val>val)cur=cur->left;
else if(cur->val<val)cur=cur->right;
}
/*这里利用parent记录父结点,就不用如上程序在移动cur时加以判断了
parent=cur=root;
while(cur)//cur指针左右动往下走,直到走到头就是该插入位置
{
parent=cur;//parent记录cur上一个结点
if(cur->val>val)cur=cur->left;
else if(cur->val<val)cur=cur->right;
}
//直接利用parent插入即可
if(val>parent->val)parent->right=node;
else parent->left=node;*/
return root;
}
//递归法
TreeNode* insertIntoBST(TreeNode* root, int val)
{
//沿着一条路(左右动)下去,到空结点返回要插结点,上层通过返回值接到左右孩子上
//注意理解这个递归通过返回值父子结点连接的问题
TreeNode*node;
node=new TreeNode(val);
if(!root)return node;
if(root->val>val)root->left=insertIntoBST(root->left,val);
else if(root->val<val)root->right=insertIntoBST(root->right,val);
return root;
}
};
二叉搜素树删除
注意理解返回值是指针的情况,这一层的返回为上层的孩子,这一层的孩子为下层返回的
450. 删除二叉搜索树中的节点
每个结点root有五种情况
1:不是要删除结点
当是删除结点时
2:左右均无 root删掉 顶上NULL
3:仅有左子树 将root删掉,顶上左子树
4:仅有右子树 将root删掉,顶上右子树
5 左右均有 将左子树嫁接到右子树的最左边叶结点的左子树上,删掉root,顶上右子树
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key)
{//此文的‘顶上’是用指针做返回值时,上层结点接住返回值,
//上层结点的孩子即为‘顶上来的’新节点
if(!root)return NULL;
TreeNode*temp;
if(root->val==key)//当前结点即为要删除结点
{
if(!root->left&&!root->right)
{//结点无左右子树,删掉后顶上NULL即可
delete root;return NULL;
}
else if(root->left&&!root->right)//判断也可省略些,为了好理解如此写全
{//结点只有左子树,将左子树顶上即可
temp=root->left;
delete(root);
return temp;
}
else if(!root->left&&root->right)
{//结点只有右子树,将结点删掉,顶上其右子树
temp=root->right;
delete(root);
return temp;
}
else //此处就是左右子树都有的情况了
{
TreeNode*cur=root->right;
while(cur->left)
{//找到root结点右子树的最左边叶结点
cur=cur->left;
}
cur->left=root->left;//将root结点左子树接到root右子树中最左结点下
temp=root->right;
delete(root);
return temp; //删掉后将右子树顶上去
}
}
//要删的不是root,继续递归删左/右,记住用root接住删除后的‘新’孩子
if(root->val>key)root->left=deleteNode(root->left,key);
if(root->val<key)root->right=deleteNode(root->right,key);
return root;
}
};
669. 修剪二叉搜索树
class Solution {
public:
//此题与删除结点不不同在于,后题只要求删除一个,删除后即回溯结束。
//但此题是删除多个,删除所有不在low,high范围内的
TreeNode* trimBST(TreeNode* root, int low, int high)
{//每个结点(每次递归)需考虑,左右子树应该是谁,应该返回谁(父的子树是谁)
if(!root)return NULL;
if(root->val<low)
{//root结点小于此区间,所以要删掉,所以返回右子树(大于根结点)顶上来的根
TreeNode* right=trimBST(root->right,low,high);
return right;//此处没free结点,仅调整指针的方向
}
if(root->val>high)
{//root结点大于此区间,所以要删掉,所以返回左子树(小于根结点)顶上来的根
TreeNode*left=trimBST(root->left,low,high);
return left;
}
//这一层root能走到这里,所以在区间内,不需要删掉,仅需确认左右子树
root->left=trimBST(root->left,low,high);
root->right=trimBST(root->right,low,high);
return root;
}
};
二叉树的公共祖先问题
还是回溯,注意返回值的设定
236二叉树的最近公共祖先
class Solution {
public:
/*本题使用回溯
先递归下去 每条路径到叶结点或指定的p,q结点往回回溯
返回值记录回溯过程中的结果信息,记录每个结点的左右子树信息
本题使用后序遍历 因为要等left和right结果出来以后才能进一步操作
*/
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
if(!root||root==p||root==q)return root;
TreeNode* left=lowestCommonAncestor(root->left,p,q);
TreeNode* right=lowestCommonAncestor(root->right,p,q);
if(!left&&!right)return NULL;//该节点左右子树中都无p,q返回NULL
if(left&&right)return root;//该结点左右子树中p,q都有,则root为结果
if(left&&!right)return left;//该节点左子树中有一个p或q,继续往前回溯
if(!left&&right)return right;//该节点有一个p或q,续往前回溯
else return NULL;
}
};
235. 二叉搜索树的最近公共祖先
class Solution {
public:
/*二叉搜素数特性使得相较于普通二叉树更精简
首先确认的是 在往下递归过程中遇到的第一个在pq值之间的值即为答案 即可返回(看图)
问题成了找位于pq值之间的元素
递归下渗过程中结点值比区间大往左走,小往右走,等于即为答案返回即可
*/
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
if(!root)return NULL;
if(root->val>q->val&&root->val>p->val)return lowestCommonAncestor(root->left,p,q);//递归下渗过程中结点值比区间大往左走,
if(root->val<q->val&&root->val<p->val)return lowestCommonAncestor(root->right,p,q);//递归下渗过程中结点值比区间小往右走,
else return root;//等于即为答案返回即可
}
};
//法二迭代法 被迭代法感动到哭
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
//迭代方法 小于区间往右走,大于往左走,遇到在区间内的返回即可
TreeNode *cur=root;
while(cur)
{
if(cur->val>p->val&&cur->val>q->val)cur=cur->left;//大于往左走
else if(cur->val<p->val&&cur->val<q->val)cur=cur->right;//大于往左走
else return cur;//遇到在区间内的返回即可
}
return NULL;
}
};