常规题
队列的最大值
题目描述
请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/dui-lie-de-zui-da-zhi-lcof
思路
使用双端队列实现单调队列,与滑动窗口类似,只是不限制窗口大小。
class MaxQueue {
public:
queue<int> data;
deque<int> maxq;
/**
使用一个单调队列(双端队列)维护数据队列的最大值
*/
MaxQueue() {
}
int max_value() {
if(data.empty())return -1;
return maxq.front();
}
void push_back(int value) {
data.push(value);
while(!maxq.empty()&&value>maxq.back()){
maxq.pop_back();
}
maxq.push_back(value);
}
int pop_front() {
if(data.empty())return -1;
int front = data.front();
//记得pop!
data.pop();
if(front==maxq.front()){
maxq.pop_front();
}
return front;
}
};
/**
* Your MaxQueue object will be instantiated and called as such:
* MaxQueue* obj = new MaxQueue();
* int param_1 = obj->max_value();
* obj->push_back(value);
* int param_3 = obj->pop_front();
*/
滑动窗口的最大值(经典双端队列实现单调栈)
题目表述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
窗口大于数组长度的时候,返回空
思路
使用双端队列实现单调队列,维护当前窗口的k个数
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int> res;
// 当给定的滑动窗口大小为0的时候 结果也是空的
if(num.size()==0||size==0)return res;
// 双端队列单调栈
deque<int> ndeque;
for(int i=0;i<size;i++){
if(ndeque.empty()){
ndeque.push_back(num[i]);
}else{
while(!ndeque.empty()&&ndeque.back()<num[i]){
ndeque.pop_back();
}
ndeque.push_back(num[i]);
}
}
for(int i=0;i<num.size()-size+1;i++){
res.push_back(ndeque.front());
if(num[i]==ndeque.front()){
ndeque.pop_front();
}
if(i+size<num.size()){
while(!ndeque.empty()&&ndeque.back()<num[i+size]){
ndeque.pop_back();
}
ndeque.push_back(num[i+size]);
}
}
return res;
/*
for(int i=size;i<num.size()-size+1;i++){
if()
}
*/
}
};
最小的k个数
题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
思路
优先队列,复杂度nlogn n是遍历数组,logn是在堆中插入数
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> res;
stack<int>resSta; // 结果需要从小到大输出, 而优先队列的pop是从大到小pop, 借助栈来完成逆序
// k等于0 结果就是空的。。
if(k>input.size()||k==0)return res;
priority_queue<int> npq; // 用于维护最小的k个数的优先队列
for(int i=0;i<k;i++){ // 先放入k个数
npq.push(input[i]);
}
for(int i=k;i<input.size();i++){
if(input[i]<npq.top()){ // top返回的是堆底最大的数
npq.pop(); // 有更小的数可以进入堆的时候, 把堆中最大的数pop
npq.push(input[i]); // 放入较小的数
}
}
while(!npq.empty()){
resSta.push(npq.top());
npq.pop();
}
while(!resSta.empty()){
res.push_back(resSta.top());
resSta.pop();
}
return res;
}
};
栈的压入、弹出序列
题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
思路
用队列和栈模拟
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
stack<int> s;
int qfr = 0;
int i=0;
while(s.empty()||s.top()!=popV[qfr]){
s.push(pushV[i++]);
while(!s.empty()&&s.top()==popV[qfr]){
qfr++;
s.pop();
}
if(i>=pushV.size()){
break;
}
}
if(s.empty()){
return true;
}else{
return false;
}
}
};
二叉搜索树的后续遍历序列
题目描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
思路
1.逆序遍历数组,先访问根节点,再访问右节点,左后访问左节点;
2.注意是二叉搜索树,左节点<根节点<右节点,使用单调栈来维护每棵子树的根节点和右节点;当左子树根节点大于其父节点时,不符合搜索树性质,返回false
class Solution {
public:
bool VerifySquenceOfBST(vector<int> sequence) {
if(sequence.size()==0)return false;
// 使用单调栈 维护(子树)根节点 和 右子树
stack<int> ns;
int curRoot = INT_MAX; // 一开始根节点设为无穷大 === 【存的是每棵左子树的【父亲】】
// 后序遍历的倒序 ---- 先序遍历的镜像 ---- 先根再右最后左
// 这样就可以先把 根和右节点 放入单调栈中维护
for(int i=sequence.size()-1;i>=0;i--){ // 外层就是一个循环放子树根节点和右节点的过程
if(sequence[i]>curRoot){
/*
当前要放入的数sequence[i],他应该是棵左子树 根节点是curRoot
在还没存放第一个左节点(就是还没有开始建第一棵左子树)的时候 curRoot应该始终为INT_MAX
*/
return false;
}
/*
当前要建左子树 要找到最接近sequence[i]的 且 大于sequence[i]的数 作为以sequence[i]为根节点的左子树的父亲。
二叉树: 左子树根节点的父亲一定是大于左子树根节点,又最接近左子树根节点
*/
// 【重点】当ns.top()<sequence[i]时 curRoot存储的就是大于 要用于建左子树的根点sequence[i] 且最接近sequence[i]的数
while(!ns.empty()&&ns.top()>sequence[i]){
curRoot = ns.top(); // 只要是大于sequence[i]都取出来试试
ns.pop(); // 这是一个循环弹出右子树和根节点准备建左子树的循环
}
// ns中的top小于sequence[i]了,只能以curRoot保存的数作为父亲了,
ns.push(sequence[i]); // 放入准备建的左子树的根节点
}
return true;
}
};
树的子结构
题目描述
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
思路
递归。
A只要包含B即可,不需要和B一模一样
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
// 可以看做把B树的根重叠在A树的根上时,A树是否包含B树结构 ====> 这里固定了两树根节点要相同
bool theSame(TreeNode* p1, TreeNode* p2){
if(p2==NULL) return true; // B树匹配空了 说明匹配完了 A树已经包含了B树 (包括B树没有左儿子或右儿子的情况)
if(p1==NULL) return false; // B树还不为空 而A树已经空了 说明A不包含B
// 当两树都还不为空
// 1.判断子树根节点是否相同
// 2.判断当前子树左子树的根节点是否相同
// 3.判断当前子树右子树的根节点是否相同
return p1->val==p2->val && theSame(p1->left,p2->left) && theSame(p1->right,p2->right);
}
// 判断以pRoot1为根的树是否包含以pRoot2为根的树 ====> 这里两树根节点可以不同
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
if(pRoot1==NULL||pRoot2==NULL)return NULL;
// 这棵树是否包含B 左子树是否包含B 右子树是否包含B
return theSame(pRoot1,pRoot2)||HasSubtree(pRoot1->left, pRoot2)||HasSubtree(pRoot1->right, pRoot2);
}
};
重建二叉树
题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
思路
详见代码注释
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
#include<map>
class Solution {
public:
map<int,int> tMap;
vector<int> preo;
vector<int> inor;
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder){
/*
递归的思想
buildSonTree(int preRooti,int inleft, int inright)
先确定根节点, 这里在preorder中找根节点下标 及其 对应的值
然后 int inleft, int inrigh 为以该根节点对应子树的建树范围
当中序遍历数组中inleft>inright 说明该子树一个节点都没有了 建树范围为空
否则建树
1.通过 根节点下标 在preorder中找到根节点对应的值 建立根节点
2.根节点的left = buildSonTree(new_root,new_inleft, new_int inright)
3.根节点的right = buildSonTree(new_root,new_inleft, new_int inright)
具体的:
【左子树】
左子树的根节点 new_root就是在当前root上偏移一个单位即可
左子树的建树范围: 要先找到当前root 在 中序遍历中的位置 位置左边部分就是建树范围
有 new_inleft = inleft; new_right = 当前root在中序遍历中的位置 - 1
【右子树】
右子树的根节点 new_root则要在当前root上偏移 【当前root在中序遍历中的位置】- inleft + 1 个单位
即要偏移【建立左子树所需要消耗的节点数量】个单位
建树范围有 new_inleft = 当前root在中序遍历中的位置 + 1; new_right = inright
最后把当前建完的子树返回即可
return t;
【备注: 递归函数的传参如果过多会使效率降低 导致超时】
*/
// TreeNode* res;
preo = preorder;
inor = inorder;
for(int i=0;i<inorder.size();i++){
tMap[inorder[i]] = i; // 存储子树的根在inorder中的位置 值是唯一的 所以可以这样取
}
return buildSonTree(0, 0, preorder.size() -1);
}
// 参数1:该子树根节点在前序遍历数组中的下标
// 参数2 3:该子树在中序遍历数组中的建树范围
TreeNode* buildSonTree(int preRooti,int inleft, int inright){
// 当中序遍历数组中inleft>inright 说明该子树一个节点都没有了 建树范围为空
if(inleft>inright)return NULL;
// 建立一个树的节点 为当前子树的根节点 先对val进行赋值(构造函数初始化)
TreeNode* t = new TreeNode(preo[preRooti]);
// 获得 该子树根节点在 中序遍历 中的位置,为建立左子树和右子树做范围准备
int iininorder = tMap[preo[preRooti]];
// preRoot+1就是左子树的下标 建树范围在 inorder 中的 inleft 到 iininorder-1
t->left = buildSonTree(preRooti+1,inleft,iininorder-1);
// 建立右子树所使用的根节点的下标要根据当前根节点在中序遍历左侧的节点数判断 即建立左子树消耗多少节点
// 在当前根节点preRooti上偏移iininorder-inleft + 1个单位
t->right = buildSonTree(preRooti + iininorder-inleft + 1 ,iininorder+1,inright);
// 把构造的子树返回
return t;
}
};
二叉搜索树的最近公共祖先
题目描述
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
思路
思考二叉搜索树的性质,左节点<根节点<右节点。
设当前节点为r
1)若 p<r<q 或者 q<r<p,即p q位于r的异侧,即r就是p q的公共祖先
2)若 r>p 且 r>q,说明p q同时位于r的左侧,它们的公共节点也就在r的左侧;
3)若 r<p 且 r<q,说明p q同时位于r的右侧,它们的公共节点也就在r的右侧;
/**
* 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) {
/*
要搞清楚什么是二叉搜索树-----左子树都是比根的值要小的数 右子树都是比根节点要大的数
设当前根节点为root,
若p和q 其中一个大于root的值 一个小于root的值 那么p q必位于root的左右子树中 异侧
若q和p两个值都大于root 则q和p都位于root的右子树中,对右子树做相同操作即可
若p和q两个值都小于root 同上
*/
TreeNode* r = root;
TreeNode* n1 = q;
TreeNode* n2 = p;
if(r==NULL||(r->left==NULL&&r->right==NULL))return NULL;
while(r!=NULL){
if((r->val>n1->val&&r->val<n2->val)||(r->val<n1->val&&r->val>n2->val)||(r==n1)||(r==n2))return r;
else if(r->val>n1->val&&r->val>n2->val)r = r->left;
else if(r->val<n1->val&&r->val<n2->val)r = r->right;
}
return r;
}
};
对称的二叉树
题目描述
请实现一个函数,用来判断一棵二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
思路
层序遍历 + 空节点的填充 + 双指针从两端向中间靠拢判断该层是否对称
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
bool isSymmetrical(TreeNode* pRoot)
{
if(pRoot==NULL) return true;
if(pRoot->left==NULL&&pRoot->right==NULL)return true;
queue<TreeNode*> tq;
TreeNode* r = pRoot;
tq.push(r);
int allNL = 0;
while(!tq.empty()&&!allNL){
int level_node_num = tq.size();
allNL = 1; // 假设下一层全为空
vector<TreeNode*> lns;
while(level_node_num--){
r = tq.front();
lns.push_back(r);
if(r!=NULL){
if(r->left!=NULL||r->right!=NULL){
allNL = 0;
/*
1 两个都不为空 都要push进去
2 一个为空 另个不为空 为空的要push一个空进去 不为空的也要push进去
总结下,都要push进去就对了
*/
tq.push(r->left);
tq.push(r->right);
}
}else{
tq.push(NULL);
tq.push(NULL);
}
tq.pop();
}
int start = 0;
int end = lns.size()-1;
for(int k=0;k<lns.size();k++){
if(lns[k]==NULL){
//printf("# ");
}
else{
//printf("%d ",lns[k]->val);
}
}
//printf("-----");
while(start<=end){
if(lns[start]==NULL&&lns[end]!=NULL){
return false;
}
if(lns[start]!=NULL&&lns[end]==NULL){
return false;
}
if(lns[start]!=NULL&&lns[end]!=NULL){
if(lns[start]->val!=lns[end]->val){
return false;
}
}
start++;
end--;
}
}
return true;
}
};
按之字形顺序打印二叉树
题目描述
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
思路
主体思路: 层序遍历
1)使用标记flag标记当前层是奇数层还是偶数层
2)奇数层是 从左向右访问的,所以需要把先访问的节点放到前面的位置:resTmp[lnn-level_node_num-1] = p->val;
3)偶数层是 从右向左访问的,所以需要把先访问的节点放到后面的位置:
resTmp[level_node_num] = p->val;
其中,lnn为该层总的节点数,level_node_num为剩下还没访问的节点数量
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int> > res;
if(pRoot==NULL)return res;
queue<TreeNode*> tq;
TreeNode* p = pRoot;
tq.push(p);
int flag = 1;
while(!tq.empty()){
int level_node_num = tq.size();
int lnn = level_node_num;
vector<int>resTmp(level_node_num,1);
while(level_node_num--){
p = tq.front();
if(flag == 1){
resTmp[lnn-level_node_num-1] = p->val;
}else{
resTmp[level_node_num] = p->val;
}
if(p->left!=NULL)tq.push(p->left);
if(p->right!=NULL)tq.push(p->right);
tq.pop();
}
res.push_back(resTmp);
if(flag==1){
flag=2;
}else{
flag=1;
}
}
return res;
}
};
二叉搜索树的第k个节点
题目描述
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
思路
方式1.二叉搜索树中序遍历第k个节点就是第k小的节点
方式2.随便使用一种遍历方式,然后用大小为k的最小堆维护
// 方式1.
TreeNode* KthNodeBypriority_PreOrder(TreeNode* pRoot, int k){
if(pRoot==NULL||k==0)return NULL;
stack<TreeNode*> ts;
int cnt=0;
TreeNode* r = pRoot;
// ts.push(r);
while(!ts.empty()||r){
while(r){
ts.push(r);
r = r->left;
}
if(!ts.empty()){
r = ts.top();
cnt++;
if(cnt==k)return r;
ts.pop();
r = r->right;
}
}
return NULL;
}
// 方式2.
TreeNode* KthNodeBypriority_queue(TreeNode* pRoot, int k)
{
// 优先队列 默认是最小堆
/*
区别:
二叉搜索树 跟 最大最小堆是不一样的
二叉搜索树左子树小于右子树;
最大最小堆堆顶的数字最大最小,使用层序存储
a.插入: 在末尾插入,不断向上比较
小于上面的值则交换,大于上面的值则停止
b.删除最小: 把末尾(最大)的元素放在堆顶, 逐层下滤
*/
priority_queue<int> pq; // 维护最小k个元素的队列
queue<TreeNode*> q; // 用于层序遍历的队列
map<int,TreeNode*>tmap;
TreeNode* r = pRoot;
if(r==NULL||k==0)return NULL;
q.push(r);
while(!q.empty()){
int level_node_num = q.size();
while(level_node_num--){
r = q.front();
tmap[r->val] = r;
if(pq.size()<k){
pq.push(r->val);
//printf("当前top: %d ",pq.top());
}else{
if(r->val<pq.top()){
pq.pop(); // 这里pop是弹出堆中最末尾的数(也就是最大的数)
pq.push(r->val);
}
//printf("当前top: %d ",pq.top());
}
if(r->left!=NULL){
q.push(r->left);
}
if(r->right!=NULL){
q.push(r->right);
}
q.pop();
}
}
if(pq.size()<k)return NULL; // 当k小于数组大小时 返回null 比如只有6个数 却要求第7小的数
return tmap[pq.top()];
}
二叉搜索树与双向链表
题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
思路
注意是 二叉搜索树, 左节点<根节点<右节点,结合中序遍历操作即可
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
TreeNode* Convert(TreeNode* pRootOfTree)
{
// 中序遍历
stack<TreeNode*> tns;
TreeNode* r = pRootOfTree;
TreeNode* res;
TreeNode* pre;
int findFirst=0;
if(pRootOfTree==NULL)return NULL;
while(r||!tns.empty()){
while(r){
tns.push(r);
r = r->left;
}
if(!tns.empty()){
r = tns.top();
if(findFirst==0){
res = r; // 第一个访问的节点就是头结点
pre = r; // pre设置为头结点
findFirst = 1; // 找到了头结点
}else{
r->left = pre; // 第二个以及之后访问的节点的r的left都是前一个节点pre
// 中序遍历最后一个访问的节点的right本来就是空 是叶子节点 左右都是NULL
pre->right = r; //前一个节点的right都是当前节点r
// res结点的left本来就是空 res指向中序遍历访问的第一个节点 是叶子节点 左右都是NULL
pre = pre->right;
}
tns.pop();
r = r->right;
}
}
return res;
}
};
平衡二叉树
题目描述
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树
思路
后序遍历,求每个节点高度,进而算出每个节点左右子树的高度差
class Solution {
public:
bool IsBalanced_Solution(TreeNode* pRoot) {
// 栈后续遍历 求子树高度
map<TreeNode*,int> thm; // 用来存放节点的高度
stack<TreeNode*> tns;
TreeNode* r = pRoot;
TreeNode* p = NULL;
while(r||!tns.empty()){
if(r!=NULL){
tns.push(r);
r = r->left;
}else{
r = tns.top();
if(r->right!=NULL&&r->right!=p){
r = r->right;
}else{
//printf("%d ",r->val);
if(r->left==NULL&&r->right==NULL){
thm[r] = 1;
}else{
if(r->left==NULL){
thm[r->left] = 0;
}
if(r->right==NULL){
thm[r->right] = 0;
}
if(thm[r->left]-thm[r->right]>1||thm[r->left]-thm[r->right]<-1){
return false;
}
if(thm[r->left]>thm[r->right]){
thm[r] = thm[r->left] + 1;
}else{
thm[r] = thm[r->right] + 1;
}
}
tns.pop();
p = r;
r = NULL;
}
}
}
return true;
}
};
1.求二叉树的深度
题目描述
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
思路
递归:树的深度等于左子树和右子树深度取大的那个+1
层序遍历:有多少层,树的深度就是多少
类似的题目还有:把二叉树打印成多行、从上往下打印二叉树
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
// 方式1.层序遍历方式
class Solution {
public:
int TreeDepth(TreeNode* pRoot)
{
int res = 0;
if(pRoot==NULL)return res;
queue<TreeNode*> tq;
TreeNode* r = pRoot;
tq.push(r);
while(!tq.empty()){
int level_node_num = tq.size();
if(level_node_num!=0)res++;
while(level_node_num--){
r = tq.front();
// 在这里执行访问节点r的操作
if(r->left!=NULL)tq.push(r->left);
if(r->right!=NULL)tq.push(r->right);
tq.pop();
}
}
return res;
}
};
// 方式2.递归方式
class Solution {
public:
int TreeDepth(TreeNode* pRoot)
{
if (!pRoot) return 0; // 终止条件: 当前节点为空时,深度为0
int ldepth = TreeDepth(pRoot->left); // 递归获得左子树的深度
int rdepth = TreeDepth(pRoot->right);// 递归获得右子树的深度
return max(lval, rval) + 1; // 左右子树深度两者取大+1为当前树的深度
}
};
2.把二叉树变换成镜像
题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。
思路
层序遍历:遍历过程中,交换当前节点的左右节点。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
// 方式1.层序遍历
class Solution {
public:
void Mirror(TreeNode *pRoot) {
if(pRoot==NULL)return;
queue<TreeNode *> tq;
TreeNode *r = pRoot;
tq.push(r);
while(!tq.empty()){
int level_node_num = tq.size();
while(level_node_num--){
r = tq.front();
// 1.先以正常的顺序放入访问队列
if(r->left!=NULL)tq.push(r->left);
if(r->right!=NULL)tq.push(r->right);
// 2.然后交换当前访问节点的左右子树位置
TreeNode *tl = r->left;
TreeNode *tr = r->right;
r->left = tr;
r->right = tl;
tq.pop();
}
}
}
};
// 方式2.递归
class Solution {
public:
TreeNode* dfs(TreeNode *r) {
if (!r) return nullptr; // 返回空指针
TreeNode *lval = dfs(r->left); // 返回左子树根节点的指针
TreeNode *rval = dfs(r->right);// 返回右子树根节点的指针
// 交换左右子树
r->left = rval;// 当前节点的左指针指向右子树指针
r->right = lval;// 当前节点的右指针指向左子树指针
return r;
}
void Mirror(TreeNode *pRoot) {
if (!pRoot) return;
dfs(pRoot);
}
};
3.二叉树中序遍历的下一个节点
题目描述
操作给定的二叉树和一个节点pNode,找出该节点在该二叉树中序遍历中的下一个节点,每个节点除了有left、right,还有一个next指向其父节点。
思路
1)pNode为空,则返回NULL;
2)pNode的右子树不为空,则pNode的下一个节点就是右子树的最左下角的那个节点;
3)pNode是某棵子树t中序遍历最后一个节点,则其下一个节点是t的父亲tRoot;而t肯定是tRoot的左儿子节点,因此,顺着pNode一直通过pNode=pNode->next的方式往上找父节点r,当r的left等于pNode的时候,r就是tRoot,就是pNode的下一个节点;
4)当pNode是整棵树中序遍历最后一个节点时返回NULL;
只有以上4中情况。
/*
struct TreeLinkNode {
int val;
struct TreeLinkNode *left;
struct TreeLinkNode *right;
struct TreeLinkNode *next;
TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
}
};
*/
class Solution {
public:
TreeLinkNode* GetNext(TreeLinkNode* pNode)
{
// 输入为给定的节点 要求该节点的中序遍历的下一个节点
// 1.该节点为空
if(pNode==NULL)return pNode;
// 情况2. pNode为某子树的根节点 中序遍历下一个节点为右子树最左节点
if(pNode->right){
pNode = pNode->right;
while(pNode->left){
pNode = pNode->left;
}
return pNode;
}
// 情况3. pNode 是某个节点左子树的中序遍历的最后一个节点
/*
pNode是某棵子树t中序遍历最后一个节点,
则其下一个节点是t的父亲tRoot;而t肯定是tRoot的左儿子节点,
因此,顺着pNode一直通过pNode=pNode->next的方式往上找父节点r,
当r的left等于pNode的时候,r就是tRoot,就是pNode的下一个节点;
*/
while(pNode->next)// 有父节点
{
// 取父节点
TreeLinkNode * r = pNode->next;
// 父节点的左儿子 与 pNode相同的时候
if(r->left==pNode){
return r;
}
// 循环追溯至 左子树的根节点
pNode = pNode->next;
}
// 情况4. pNode为中序遍历最后一个节点
return NULL;
}
};