算法笔记9.2 二叉树的遍历 及 中序分别与先序、后序、层序搭配构建二叉树

目录

9.2.1先序遍历

9.2.2 中序遍历

9.2.3后序遍历

9.2.4层序遍历

9.2.5根据先序中序序列,建立二叉树

        变式.根据后序中序序列还原二叉树

        变式:根据层序中序序列还原二叉树

9.2.6二叉树的静态实现



 

二叉树的遍历共四种:先序遍历、中序遍历、后序遍历、层次遍历
前三种:  采用深度优先搜索(DFS)实现   递归
层次遍历: 采用广度优先搜索(BFS)实现    队列

二叉树的递归定义和前三种遍历方式很好地融合了

一定记住:对于前三种遍历(先序,中序,后序)左子树一定先于右子
         树遍历
,所谓的“先中后”指的是根结点root在遍历中的位置
因此:
     先序遍历:根结点->左子树->右子树
     中序遍历:左子树->根结点->右子树
     后序遍历:左子树->右子树->根结点

     

 

9.2.1先序遍历

先序遍历的实现

9-7先序序列:ABDECF
先序遍历序列的第一个元素一定是根结点

//先根遍历
void preorder(node* root){
	if(root==NULL) return;//到达空树,递归边界
	//访问根结点root,如输出数据域
	cout<<root->data<<" ";
	//访问左子树
	preorder(root->lchild);
	//访问右子树
	preorder(root->rchild);
}

 

先序创建,因为心浮气躁,卡了好久,太艰难了。。。一定要冷静分析demo的代码

//创建树,先序插入  #表示结点为空
void CreateBiTree(node* &root)
{
    char c;
    cin>>c;
 
    if(c == '#')
        root = NULL;  //null表示为空枝
    else
    {
        root = new node;
        root->data = c;
        CreateBiTree(root->lchild);
        CreateBiTree(root->rchild);
    }
}

实例:

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;

struct node{
	char data;//数据域  类型此处默认设为char
	node* lchild;//指向左子树根结点的指针
	node* rchild;//指向右子树根结点的指针	
};

//新建结点(插入结点时用)
//生成一个新结点,v为结点的权值
node* newNode(char v){
	node* Node=new node;//申请一个node型变量的地址空间
	Node->data=v;
	Node->lchild=Node->rchild=NULL;//初始状态下没有左右孩子
	return Node;
}

//先序插入遍历
void pre_Create(node* &root)
{
    char c;
    cin>>c;
 
    if(c == '#')
        root = NULL;  //null表示为空枝
    else
    {
        root = new node;
        root->data = c;
        pre_Create(root->lchild);
        pre_Create(root->rchild);
    }
}

//先根遍历
void preorder(node* root){
	if(root==NULL) return;//到达空树,递归边界
	//访问根结点root,如输出数据域
	cout<<root->data;
	//访问左子树
	preorder(root->lchild);
	//访问右子树
	preorder(root->rchild);
}

//层序遍历
void LayerOrder(node* root){
	queue<node*> q;
	q.push(root);
	while(!q.empty()){
		node* top=q.front();
		q.pop();
		cout<<top->data;
		if(top->lchild!=NULL) q.push(top->lchild);
		if(top->rchild!=NULL) q.push(top->rchild);
	}
}

int main(){
	freopen("input1.txt","r",stdin);
	node* root;
	pre_Create(root);//先根插入 创建二叉树
	
	cout<<"先序遍历:";
	preorder(root);
	cout<<endl;

	cout<<"层序遍历:";
	LayerOrder(root);
	cout<<endl;

	return 0;
}

input1.txt

ABD##E##C#F##

运行结果:

 

9.2.2 中序遍历

左子树->根结点->右子树
只要知道根结点,就可以通过根结点在中序遍历序列中的位置区分出左子树和右子树
中序序列:DBEACF

中序遍历代码:

//中根遍历
void preorder(node* root){
	if(root==NULL) return;//到达空树,递归边界
	//访问左子树
	preorder(root->lchild);
	//访问根结点root,如输出数据域
	cout<<root->data;
	//访问右子树
	preorder(root->rchild);
}

样例:

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;

struct node{
	char data;//数据域  类型此处默认设为char
	node* lchild;//指向左子树根结点的指针
	node* rchild;//指向右子树根结点的指针	
};

//新建结点(插入结点时用)
//生成一个新结点,v为结点的权值
node* newNode(char v){
	node* Node=new node;//申请一个node型变量的地址空间
	Node->data=v;
	Node->lchild=Node->rchild=NULL;//初始状态下没有左右孩子
	return Node;
}

//先序插入遍历
void pre_Create(node* &root){
    char c;
    cin>>c; 
    if(c == '#')
        root = NULL;  //null表示为空枝
    else{
        root = new node;
        root->data = c;
        pre_Create(root->lchild);
        pre_Create(root->rchild);
    }
}

//中根遍历
void preorder(node* root){
	if(root==NULL) return;//到达空树,递归边界
	//访问左子树
	preorder(root->lchild);
	//访问根结点root,如输出数据域
	cout<<root->data;
	//访问右子树
	preorder(root->rchild);
}

//层序遍历
void LayerOrder(node* root){
	queue<node*> q;
	q.push(root);
	while(!q.empty()){
		node* top=q.front();
		q.pop();
		cout<<top->data;
		if(top->lchild!=NULL) q.push(top->lchild);
		if(top->rchild!=NULL) q.push(top->rchild);
	}
}

int main(){
	freopen("input1.txt","r",stdin);
	node* root;
	pre_Create(root);//先根插入 创建二叉树
	
	cout<<"中序遍历:";
	preorder(root);
	cout<<endl;

	cout<<"层序遍历:";
	LayerOrder(root);
	cout<<endl;

	return 0;
}

input1.txt

ABD##E##C#F##

运行结果:

 

 

9.2.3后序遍历

左子树->右子树->根结点
样例后序序列:DEBFCA
后序序列的最后一个结点一定是根结点
★:无论是先序遍历序列还是后序遍历序列都必须知道中序遍历序列才能唯一确定一颗树
   因为通过先序遍历序列和后序遍历序列都只能得到根结点,而只有通过中序遍历序列
   才能利用根结点把左右子树分开,从而递归生成一棵二叉树(需要所有元素互不相同时才能使用)

后序遍历:

//后根遍历
void preorder(node* root){
	if(root==NULL) return;//到达空树,递归边界
	//访问左子树
	preorder(root->lchild);
	//访问右子树
	preorder(root->rchild);
	//访问根结点root,如输出数据域
	cout<<root->data;
}

样例:

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;

struct node{
	char data;//数据域  类型此处默认设为char
	node* lchild;//指向左子树根结点的指针
	node* rchild;//指向右子树根结点的指针	
};

//新建结点(插入结点时用)
//生成一个新结点,v为结点的权值
node* newNode(char v){
	node* Node=new node;//申请一个node型变量的地址空间
	Node->data=v;
	Node->lchild=Node->rchild=NULL;//初始状态下没有左右孩子
	return Node;
}

//先序插入遍历
void pre_Create(node* &root){
    char c;
    cin>>c; 
    if(c == '#')
        root = NULL;  //null表示为空枝
    else{
        root = new node;
        root->data = c;
        pre_Create(root->lchild);
        pre_Create(root->rchild);
    }
}

//后根遍历
void preorder(node* root){
	if(root==NULL) return;//到达空树,递归边界
	//访问左子树
	preorder(root->lchild);
	//访问右子树
	preorder(root->rchild);
	//访问根结点root,如输出数据域
	cout<<root->data;
}

//层序遍历
void LayerOrder(node* root){
	queue<node*> q;
	q.push(root);
	while(!q.empty()){
		node* top=q.front();
		q.pop();
		cout<<top->data;
		if(top->lchild!=NULL) q.push(top->lchild);
		if(top->rchild!=NULL) q.push(top->rchild);
	}
}

int main(){
	freopen("input1.txt","r",stdin);
	node* root;
	pre_Create(root);//先根插入 创建二叉树
	
	cout<<"后序遍历:";
	preorder(root);
	cout<<endl;

	cout<<"层序遍历:";
	LayerOrder(root);
	cout<<endl;

	return 0;
}

input1.txt:

ABD##E##C#F##

运行结果:

 

9.2.4层序遍历

从上到下,从左到右 BFS
层序序列:ABCDEF

//层序遍历
void LayerOrder(node* root){
	queue<node*> q;
	q.push(root);
	while(!q.empty()){
		node* top=q.front();
		q.pop();
		cout<<top->data;
		if(top->lchild!=NULL) q.push(top->lchild);
		if(top->rchild!=NULL) q.push(top->rchild);
	}
}

上面每个样例都有层序输出

 

带层次的层序输出:

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;

struct node{
	char data;//数据域  类型此处默认设为char
	int layer;//层次
	node* lchild;//指向左子树根结点的指针
	node* rchild;//指向右子树根结点的指针	
};

//新建结点(插入结点时用)
//生成一个新结点,v为结点的权值
node* newNode(char v){
	node* Node=new node;//申请一个node型变量的地址空间
	Node->data=v;
	Node->lchild=Node->rchild=NULL;//初始状态下没有左右孩子
	return Node;
}

//先序插入遍历
void pre_Create(node* &root){
    char c;
    cin>>c; 
    if(c == '#')
        root = NULL;  //null表示为空枝
    else{
        root = new node;
        root->data = c;
        pre_Create(root->lchild);
        pre_Create(root->rchild);
    }
}

//层序遍历
void LayerOrder(node* root){
	queue<node*> q;
	root->layer=1;//根的层次为1
	q.push(root);
	while(!q.empty()){
		node* top=q.front();
		q.pop();
		cout<<top->layer<<":"<<top->data<<" ";
		if(top->lchild!=NULL){
			top->lchild->layer=top->layer+1;
			q.push(top->lchild);
		} 
		if(top->rchild!=NULL){
			top->rchild->layer=top->layer+1;
			q.push(top->rchild);
		}
	}
}

int main(){
	freopen("input1.txt","r",stdin);
	node* root;
	pre_Create(root);//先根插入 创建二叉树
	
	cout<<"层序遍历:";
	LayerOrder(root);
	cout<<endl;

	return 0;
}

运行结果:

 

9.2.5根据先序中序序列,建立二叉树

eg:
先:ABDECF
中:DBEACF

每次先序第一个提供根结点,中序根据根结点分割左右子树,
同样方式处理左右子树(先确定左右子树先后序列范围)如此反复

层序:ABCDEF

 

#include<iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef char Type;

Type pre[100],in[100];

struct node{
	Type data;
	node* lchild;
	node* rchild;	
};

node* newNode(Type x){
	node* Node=new node;
	Node->data=x;
	Node->lchild=Node->rchild=NULL;
	return Node;
}

//先序[preL,preR]  中序:[inL,inR]
node* Create(int preL,int preR,int inL,int inR){
	if(preL>preR) return NULL;//先序序列为空 结束
	node* root=newNode(pre[preL]);//根结点 先序第一个
	//中序中找到根结点位置以分割左右子树
	int k;
	for(k=inL;k<=inR;k++){
		if(in[k]==pre[preL]) break;//所有结点data互不相同 否则出bug
	}
	//找到k即为根结点在中序中的位置下标
	//分割左右子树
	int numLeft=k-inL;//左子树长度

	//递归求左右子树 
	//左子树先序中序序列范围  第一个pre是根结点
	root->lchild=Create(preL+1,preL+numLeft,inL,k-1);
	//右子树先序中序序列范围  k是中序根结点
	root->rchild=Create(preL+numLeft+1,preR,k+1,inR);

	return root;//返回根结点地址
}

//先序遍历
void preOrder(node* root){
	if(root==NULL) return;
	cout<<root->data;
	preOrder(root->lchild);
	preOrder(root->rchild);
}

//中序遍历
void inOrder(node* root){
	if(root==NULL) return;
	inOrder(root->lchild);
	cout<<root->data;
	inOrder(root->rchild);
}

//后序遍历
void postOrder(node* root){
	if(root==NULL) return;
	postOrder(root->lchild);
	postOrder(root->rchild);
	cout<<root->data;
}

//层序遍历
void layerOrder(node* root){
	queue<node*> q;
	q.push(root);
	while(!q.empty()){
		node* top=q.front();
		q.pop();
		cout<<top->data;
		if(top->lchild!=NULL) q.push(top->lchild);
		if(top->rchild!=NULL) q.push(top->rchild);
	}
}

int main(){
	freopen("input5.txt","r",stdin);
	cin>>pre>>in;
	int preL,preR,inL,inR;
	preL=0,preR=strlen(pre)-1,inL=0,inR=strlen(in)-1;
	// cout<<preL<<" "<<preR<<" "<<inL<<" "<<inR<<endl;
	node* root=Create(preL,preR,inL,inR);
	cout<<"先序遍历:";preOrder(root);cout<<endl;
	cout<<"中序遍历:";inOrder(root);cout<<endl;
	cout<<"后序遍历:";postOrder(root);cout<<endl;
	cout<<"层序遍历:";layerOrder(root);cout<<endl;
	return 0;
}

input5.txt

ABDECF
DBEACF

//现在不要死扣太多,到时候系统地复习数据结构时就全懂了,而且很快!

 

变式.根据后序中序序列还原二叉树

#include<iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef char Type;

Type post[100],in[100];

struct node{
	Type data;
	node* lchild;
	node* rchild;	
};

//后序[postL,postR]  中序:[inL,inR]
node* Create(int postL,int postR,int inL,int inR){
	if(postL>postR) return NULL;//后序序列为空 结束
	node* root=new node;//根结点   new node不能少 否则就只是分配一个野指针 无法使用->
	root->data=post[postR];//后序第一个
	//中序中找到根结点位置以分割左右子树
	int k;
	for(k=inL;k<=inR;k++){
		if(in[k]==post[postR]) break;//所有结点data互不相同 否则出bug
	}
	//找到k即为根结点在中序中的位置下标
	//分割左右子树
	int numLeft=k-inL;//左子树长度

	//递归求左右子树 
	//左子树先序中序序列范围  最后一个post序列是根结点
	root->lchild=Create(postL,postL+numLeft-1,inL,k-1);
	//右子树先序中序序列范围  k是中序根结点
	root->rchild=Create(postL+numLeft,postR-1,k+1,inR);

	return root;//返回根结点地址
}

//先序遍历
void preOrder(node* root){
	if(root==NULL) return;
	cout<<root->data;
	preOrder(root->lchild);
	preOrder(root->rchild);
}

//中序遍历
void inOrder(node* root){
	if(root==NULL) return;
	inOrder(root->lchild);
	cout<<root->data;
	inOrder(root->rchild);
}

//后序遍历
void postOrder(node* root){
	if(root==NULL) return;
	postOrder(root->lchild);
	postOrder(root->rchild);
	cout<<root->data;
}

//层序遍历
void layerOrder(node* root){
	queue<node*> q;
	q.push(root);
	while(!q.empty()){
		node* top=q.front();
		q.pop();
		cout<<top->data;
		if(top->lchild!=NULL) q.push(top->lchild);
		if(top->rchild!=NULL) q.push(top->rchild);
	}
}

int main(){
	freopen("input5_1.txt","r",stdin);
	cin>>post>>in;
	int postL,postR,inL,inR;
	postL=0,postR=strlen(post)-1,inL=0,inR=strlen(in)-1;
	// cout<<postL<<" "<<postR<<" "<<inL<<" "<<inR<<endl;
	node* root=Create(postL,postR,inL,inR);
	cout<<"先序遍历:";preOrder(root);cout<<endl;
	cout<<"中序遍历:";inOrder(root);cout<<endl;
	cout<<"后序遍历:";postOrder(root);cout<<endl;
	cout<<"层序遍历:";layerOrder(root);cout<<endl;
	return 0;
}

input5_1.txt

DEBFCA
DBEACF

 

变式:根据层序中序序列还原二叉树

原理一样(层序第一个是根结点,还是提供根),关键找到左右子树的层序和中序序列,此时由于下次左右子树的层序序列不是连续排列的,需要重新建立数组存储下来再传递(先序后序序列一定一样,不过元素顺序不同而已),仅此而已

#include<iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef char Type;

const int MAXN=100;

struct node{
	Type data;
	node* lchild;
	node* rchild;	
};

//后序[postL,postR]  中序:[inL,inR]
node* Create(Type layer[],Type in[],int len){//长度都一样 一个len即可
	if(len==0) return NULL;
	node* root=new node;
	root->data=layer[0];
	int k;
	//中序中找根
	for(k=0;k<len;k++){
		if(in[k]==layer[0]) break;
	}
	//new节省空间  申请左右子树的层序即可 中序可由指针加法移位获得
	Type *Llay=new Type[len],*Rlay=new Type[len];
	
	//左子树的先序序列  
	/*中序都很简单根两边就是  难找的就是这个先序 
	好在只是中序顺序换了下 根据中序在整个先序里找
	重复的即可,此处也可看出序列data不能有相同*/
	int cnt=0;
	for(int i=1;i<len;i++){//第0个根结点,i从1开始即可
		for(int j=0;j<k;j++){
			if(layer[i]==in[j]){//in[j]不是in[k] 别犯傻
				Llay[cnt++]=layer[i];
				break;//不可能有重复
			}
		}
	}
	root->lchild=Create(Llay,in,cnt);

	cnt=0;
	for(int i=1;i<len;i++){
		for(int j=k+1;j<len;j++){
			if(layer[i]==in[j]){//in[j]不是in[k] 别犯傻
				Rlay[cnt++]=layer[i];
				break;//不可能有重复
			}
		}
	}
	root->rchild=Create(Rlay,in+k+1,cnt);

	return root;//返回根结点地址
}

//先序遍历
void preOrder(node* root){
	if(root==NULL) return;
	cout<<root->data;
	preOrder(root->lchild);
	preOrder(root->rchild);
}

//中序遍历
void inOrder(node* root){
	if(root==NULL) return;
	inOrder(root->lchild);
	cout<<root->data;
	inOrder(root->rchild);
}

//后序遍历
void postOrder(node* root){
	if(root==NULL) return;
	postOrder(root->lchild);
	postOrder(root->rchild);
	cout<<root->data;
}

//层序遍历
void layerOrder(node* root){
	queue<node*> q;
	q.push(root);
	while(!q.empty()){
		node* top=q.front();
		q.pop();
		cout<<top->data;
		if(top->lchild!=NULL) q.push(top->lchild);
		if(top->rchild!=NULL) q.push(top->rchild);
	}
}

int main(){
	freopen("input5_2.txt","r",stdin);
	Type layer[MAXN],in[MAXN];
	cin>>layer>>in;
	int len=strlen(layer);
	cout<<len<<endl;
	node* root=Create(layer,in,len);
	cout<<"先序遍历:";preOrder(root);cout<<endl;
	cout<<"中序遍历:";inOrder(root);cout<<endl;
	cout<<"后序遍历:";postOrder(root);cout<<endl;
	cout<<"层序遍历:";layerOrder(root);cout<<endl;
	return 0;
}

input5_2.txt

ABCDEF
DBEACF

 

说明:中序序列可以与先序序列、后序序列、层序序列中的任意一个来构建唯一的二叉树,而后者两两搭配或是三个一起上都无法构建唯一的二叉树。原因是先序、后序、层序均是提供根结点,作用是相同的,都必须由中序序列来区分出左右子树。

 

9.2.6二叉树的静态实现

#include<iostream>
#include<queue>
using namespace std;

typedef int Type;
const int MAXN=100;
struct node{
	Type data;
	int lchild,rchild;
}Node[MAXN];

int index=0;
int newNode(int v){
	Node[index].data=v;
	Node[index].lchild=Node[index].rchild=-1;
	return index++;
}

void search(int root,int x,int newdata){
	if(root==-1) return;
	if(Node[root].data==x){
		Node[root].data=newdata;//不return 符合条件的都该
	}
	search(Node[root].lchild,x,newdata);
	search(Node[root].rchild,x,newdata);
}

void insert(int &root,int x){
	if(root==-1){//注意
		root=newNode(x);//改root指向的那块内存
		return;
	}
	//坑人地不给代码
	if(/*由二叉树的性质x应该插在左子树*/){
		insert(Node[root].lchild,x);
	}else{
		insert(Node[root].rchild,x);
	}
}

//坑人的建立,函数返回根结点root下标
int Create(int data[],int n){
	int root=-1;
	for(int i=0;i<n;i++){
		insert(root,data[i]);
	}
	return root;
}

//先序遍历
void preorder(int root){
	if(root==-1) return;
	cout<<Node[root].data;//非指针而是结构体变量直接用.
	preorder(Node[root].lchild);
	preorder(Node[root].rchild);
}

//中序遍历
void inorder(int root){
	if(root==-1) return;
	preorder(Node[root].lchild);
	cout<<Node[root].data;//非指针而是结构体变量直接用.
	preorder(Node[root].rchild);
}

//后序遍历
void postorder(int root){
	if(root==-1) return;
	preorder(Node[root].lchild);
	preorder(Node[root].rchild);
	cout<<Node[root].data;//非指针而是结构体变量直接用.
}

//层序遍历
void layerorder(int root){
	queue<int> q;
	q.push(root);
	while(!q.empty()){
		int top=q.front();
		q.pop();
		cout<<Node[top].data<<" ";
		if(Node[top].lchild!=-1) q.push(Node[top].lchild);
		if(Node[top].rchild!=-1) q.push(Node[top].rchild);
	}
}


int main(){
	/*不会test  实在不知道更具二叉树的什么性质应该插在左子树*/
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值