我有一瓢酒,可以慰风尘
题目
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点
输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]
思路讲解:
首先写一种比较好理解的 也就是递归法,递归只需要处理好当前结点就可以了,直接交换指针域,不是数据域 因为可能一个孩子, 这里输出与交换这里是分开来写的, 但是笔者在写这个的时候想一个事情,这个深入次序有讲究吗?(也就是这两句Preorder(T->Lchild) Preorder(T->Rchild)其实是什么次序都可以的,至于原因希望大家思考一波,也不难)
下面给出代码 先于深入之前操作, 这里就叫做前序吧
前序
#include<bits/stdc++.h>
#define ElemType int
using namespace std;
typedef struct BiNTree{
struct BiNTree* Lchild;
ElemType data;
struct BiNTree* Rchild;
}BiNTree;
/*层序的思想是,首先需要判断此时第一个元素是否为空 为此
若是为空就不用需要进入树队中了 若是第一个元素不为空 则进入队列中
然后为其分配左右子树,若是值为-1 则表示此时的孩子为空 不需要入队
若是值不为-1 ,让其作为此时结点的孩子,然后此孩子入队
直到元素值队列为空*/
void Creat(BiNTree* &T){
int val;
BiNTree* cur;//定义一个cur
queue<ElemType> Q;
queue<BiNTree*> TreeQue;
cout<<"请输入层序遍历的序列"<<endl;
while(scanf("%d",&val)!=EOF) Q.push(val);
if(Q.front()==-1){
cout<<"树为空"<<endl;
T=NULL;
return ;
}
else{
T=(BiNTree*)malloc(sizeof(BiNTree));
T->data=Q.front();
TreeQue.push(T);
}
Q.pop();
while(!Q.empty()){//我们每一次处理 处理的都是左右孩子,所以也就需要两个if
cur=TreeQue.front();//取出此时树队中的第一元素
if(Q.front()==-1){//此时这个结点值为NULL
cout<<cur->data<<"左孩子为空"<<endl;
cur->Lchild=NULL;
}
else{//新生成一个树结点用来存放此时的结点信息
cout<<cur->data<<"左孩子为"<<Q.front()<<endl;
cur->Lchild=(BiNTree*)malloc(sizeof(BiNTree)) ;
cur->Lchild->data=Q.front();
TreeQue.push(cur->Lchild);
}
Q.pop();//处理完数据之后需要弹出 来取此结点右子树的值
//上面的if else 是处理结点左孩子 下面我们来处理右孩子
if(Q.front()==-1){
cout<<cur->data<<"右孩子为空"<<endl;;
cur->Rchild=NULL;
}
else{
cout<<cur->data<<"右孩子为"<<Q.front()<<endl;
cur->Rchild=(BiNTree*)malloc(sizeof(BiNTree));
cur->Rchild->data=Q.front();
TreeQue.push(cur->Rchild);
}
Q.pop();
TreeQue.pop();
}
}
void Preorder(BiNTree* &T){
if(T==NULL){
return;
}
BiNTree* cur=NULL;
// T->Rchild->data^=T->Lchild->data;
// T->Lchild->data^=T->Rchild->data;
// T->Rchild->data^=T->Lchild->data;
// a^=b;
// b^=a;
// a^=b;
/*本来写的是交换数据域 但是想了一下 好像有的可能只有一个孩子 直接交换当前结点的指针域的指向即可*/
cur=T->Lchild;//就跟数据交换一个差不多 需要先保存
//cout<<T->data<<" ";
T->Lchild=T->Rchild;
T->Rchild=cur;
Preorder(T->Lchild);//这里需要左右子树的次序考虑一下次序?
Preorder(T->Rchild);
}
void LevelOrder(BiNTree* T){
if(T==NULL){
return;
}
queue<BiNTree*> Q; BiNTree* cur;
Q.push(T);
while(!Q.empty()){
int size=Q.size();
for(int i=0;i<size;i++){
cur=Q.front();Q.pop();
cout<<cur->data<<" ";
if(cur->Lchild) Q.push(cur->Lchild);
if(cur->Rchild) Q.push(cur->Rchild);
}
}
}
main(){
BiNTree* T=NULL;
Creat(T);
cout<<"此时树的层序遍历是"<<endl;
Preorder(T);
LevelOrder(T);
}
后序
后于结点操作 这里姑且叫做后序,深入次序依然和前序一样,两种都可以;
void Preorder(BiNTree* &T){
if(T==NULL){
return;
}
BiNTree* cur=NULL;
// T->Rchild->data^=T->Lchild->data;
// T->Lchild->data^=T->Rchild->data;
// T->Rchild->data^=T->Lchild->data;
// a^=b;
// b^=a;
// a^=b;
/*本来写的是交换数据域 但是想了一下 好像有的可能只有一个孩子 直接交换当前结点的指针域的指向即可*/
Preorder(T->Lchild);
Preorder(T->Rchild);
cur=T->Lchild;//就跟数据交换一个差不多 需要先保存
//cout<<T->data<<" ";
T->Lchild=T->Rchild;
T->Rchild=cur;
}
中序
若是你以为中序是这样(错误版本)
void Preorder(BiNTree* &T){
if(T==NULL){
return;
}
BiNTree* cur=NULL;
// T->Rchild->data^=T->Lchild->data;
// T->Lchild->data^=T->Rchild->data;
// T->Rchild->data^=T->Lchild->data;
// a^=b;
// b^=a;
// a^=b;
/*本来写的是交换数据域 但是想了一下 好像有的可能只有一个孩子 直接交换当前结点的指针域的指向即可*/
Preorder(T->Lchild);
cur=T->Lchild;//就跟数据交换一个差不多 需要先保存
//cout<<T->data<<" ";
T->Lchild=T->Rchild;
T->Rchild=cur; 
cout<<"此时交换的是"<<T->data<<"的左右孩子"<<endl;
Preorder(T->Rchild);
}
可以看出有的结点执行两次 有的结点没有执行 原因是交换之后 你在深入你原本以为的右子树 在已经被偷偷换成了左子树, 而左子树你刚才已经操作过了 所以也就会深入两遍 你既然知道的原因是偷偷给我偷梁换柱了,所以找到原来的梁 原来的梁被它悄悄换到哪里了,,我认为应该是左子树上 所以继续深入左子树也就可以了,于是可写
(正确版本)
void Preorder(BiNTree* &T){
if(T==NULL){
return;
}
BiNTree* cur=NULL;
// T->Rchild->data^=T->Lchild->data;
// T->Lchild->data^=T->Rchild->data;
// T->Rchild->data^=T->Lchild->data;
// a^=b;
// b^=a;
// a^=b;
/*本来写的是交换数据域 但是想了一下 好像有的可能只有一个孩子 直接交换当前结点的指针域的指向即可*/
Preorder(T->Lchild);
cur=T->Lchild;//就跟数据交换一个差不多 需要先保存
//cout<<T->data<<" ";
T->Lchild=T->Rchild;
T->Rchild=cur;
cout<<"此时交换的是"<<T->data<<"的左右孩子"<<endl;
Preorder(T->Lchild);
}
中序迭代
这里我们在写一种迭代版本吧 写哪一种遍历次序呢?人最烦的就是这一点了,都会就不知道怎么选了 要是只会递归就不用想这么多了,要是只会一种迭代 也不用想这么多了 哎。。 中序不统一 这里就写中序吧, 其他两种请看这篇修改一点即可 这里我也重新写一遍 希望大家不要复制 自己也写一遍 不会的话 最起码也要思考后才行,不要复制,不要复制 这些遍历很重要!!! 想不出来怎么开始 提示:迭代是使用栈操作的。。
#include<bits/stdc++.h>
#define ElemType int
using namespace std;
typedef struct BiNTree{
struct BiNTree* Lchild;
ElemType data;
struct BiNTree* Rchild;
}BiNTree;
/*层序的思想是,首先需要判断此时第一个元素是否为空 为此
若是为空就不用需要进入树队中了 若是第一个元素不为空 则进入队列中
然后为其分配左右子树,若是值为-1 则表示此时的孩子为空 不需要入队
若是值不为-1 ,让其作为此时结点的孩子,然后此孩子入队
直到元素值队列为空*/
void Creat(BiNTree* &T){
int val;
BiNTree* cur;//定义一个cur
queue<ElemType> Q;
queue<BiNTree*> TreeQue;
cout<<"请输入层序遍历的序列"<<endl;
while(scanf("%d",&val)!=EOF) Q.push(val);
if(Q.front()==-1){
cout<<"树为空"<<endl;
T=NULL;
return ;
}
else{
T=(BiNTree*)malloc(sizeof(BiNTree));
T->data=Q.front();
TreeQue.push(T);
}
Q.pop();
while(!Q.empty()){//我们每一次处理 处理的都是左右孩子,所以也就需要两个if
cur=TreeQue.front();//取出此时树队中的第一元素
if(Q.front()==-1){//此时这个结点值为NULL
cout<<cur->data<<"左孩子为空"<<endl;
cur->Lchild=NULL;
}
else{//新生成一个树结点用来存放此时的结点信息
cout<<cur->data<<"左孩子为"<<Q.front()<<endl;
cur->Lchild=(BiNTree*)malloc(sizeof(BiNTree)) ;
cur->Lchild->data=Q.front();
TreeQue.push(cur->Lchild);
}
Q.pop();//处理完数据之后需要弹出 来取此结点右子树的值
//上面的if else 是处理结点左孩子 下面我们来处理右孩子
if(Q.front()==-1){
cout<<cur->data<<"右孩子为空"<<endl;;
cur->Rchild=NULL;
}
else{
cout<<cur->data<<"右孩子为"<<Q.front()<<endl;
cur->Rchild=(BiNTree*)malloc(sizeof(BiNTree));
cur->Rchild->data=Q.front();
TreeQue.push(cur->Rchild);
}
Q.pop();
TreeQue.pop();
}
}
void InOrder(BiNTree* &T){
/*首先我们知道使用的是栈来操作的 栈的特点是先进后出,我们需要的次序是 左中右 所以进栈应该为 右中左,因为是中序 所以也就是第二次
经过的时候进行处理的,所以这里需要一个标记NULL 第一个经过的时候 在其后加一个标签 说明下一个结点是第二次遇到了 可以访问了*/
stack<BiNTree*> S; BiNTree* cur;BiNTree* temp;
if(T==NULL){
return;
}
S.push(T);
while(!S.empty()){// 当前栈不为空 说明没有遍历完
cur=S.top();
if(cur==NULL){//说明下一个结点第二次访问了
S.pop();//将NULL弹出
//cout<<S.top()->data;//第二次访问这个结点
temp=S.top()->Lchild;//就跟数据交换一个差不多 需要先保存
//cout<<T->data<<" ";
S.top()->Lchild=S.top()->Rchild;
S.top()->Rchild=temp;
S.pop();//再次弹出
}
else{//此时这个结点是第一次访问 将其右孩子入栈,中间结点入栈并标记,左孩子入栈,
S.pop();//也是要弹出再装入
if(cur->Rchild) S.push(cur->Rchild);
S.push(cur);
S.push(NULL);//要后入 后入先出 才能判断下一个是不是已经访问过了
if(cur->Lchild) S.push(cur->Lchild);
}
}
}
void LevelOrder(BiNTree* T){
if(T==NULL){
return;
}
queue<BiNTree*> Q; BiNTree* cur;
Q.push(T);
while(!Q.empty()){
int size=Q.size();
for(int i=0;i<size;i++){
cur=Q.front();Q.pop();
cout<<cur->data<<" ";
if(cur->Lchild) Q.push(cur->Lchild);
if(cur->Rchild) Q.push(cur->Rchild);
}
}
}
main(){
BiNTree* T=NULL;
Creat(T);
cout<<"此时树的层序遍历是"<<endl;
InOrder(T);
LevelOrder(T);
}
可以看到我中间的部分写的不一样,笔者写这个一遍过 若是问我为什么这么厉害 若是让你好受一点的话 我可以回答是天赋 哈哈哈哈