翻转二叉树

本文介绍了如何通过递归和迭代的方式实现二叉树的翻转,并探讨了前序、中序遍历的不同次序处理策略。作者详细展示了从层序遍历创建二叉树到不同遍历顺序的转换过程,适合理解和实践树结构操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我有一瓢酒,可以慰风尘

请添加图片描述

题目

给你一棵二叉树的根节点 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; ![请添加图片描述](https://img-blog.csdnimg.cn/14162fd8f8bc44728cf16f7ab9d0a603.png)

	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);
}

可以看到我中间的部分写的不一样,笔者写这个一遍过 若是问我为什么这么厉害 若是让你好受一点的话 我可以回答是天赋 哈哈哈哈
请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值