数据结构15————二叉树的遍历和建立

数据结构15————二叉树的遍历和建立

一.内容

  1. 二叉树遍历的概念
  2. 二叉树的遍历 使用递归实现
  3. 二叉树的遍历 使用栈实现
  4. 二叉树的建立
  5. 二叉树遍历的应用

所有代码只是核心函数,完整代码见末尾链接
注意本篇博客都是基于二叉链表的实现

二.二叉树遍历的概念

如果我们要求次序的不重复的,遍历一颗树,并且限制从左到右习惯方式,一共有4种不同遍历方法。
这里写图片描述

1.层次遍历

按照二叉树的层,一层一层的访问。

例:对于二叉树T而言,它的层次遍历序列为ABCDEFG

2.前序遍历

对于一颗二叉树

  • 先访问根节点
  • 前序遍历左子树(按照前序遍历规则访问左子树,即先访问左子树的根,然后继续遍历它的左右子树,下同)
  • 前序遍历右子树

例:对于二叉树T而言,它的前序遍历序列为ABDGCEF

3.中序遍历

对于一颗二叉树

  • 中序遍历它的左子树
  • 访问它的根节点
  • 中序遍历它的右子树

例:对于二叉树T而言,它的中序遍历序列为DGBAECF

4.后序遍历

对于一颗二叉树

  • 后序遍历它的左子树
  • 后序遍历它的右子树
  • 访问根节点

例:对于二叉树T而言,它的后序遍历序列为GDBEFCA

三.二叉树的遍历 递归实现

其实根据上面的前中后序遍历规则,基本来说就可以确定递归的写法了,不过要增加一个递归出口。当前指针为NULL时,结束。

1.前序遍历
void PreOrder(BiTree root){
	int static count;
	if(root==NULL)
		return;
		
	printf("%c",root->data);
	PreOrder(root->lChild);
	PreOrder(root->rChild);
}
2.中序遍历
void InOrder(BiTree root){
	if(root==NULL)
		return;
		
	InOrder(root->lChild);
	printf("%c",root->data);
	InOrder(root->rChild);
}
3.后序遍历
void PostOrder(BiTree root){
	if(root==NULL)
		return;
		
	PostOrder(root->lChild);
	PostOrder(root->rChild);
	printf("%c",root->data);
}
4.层次遍历

对于层次遍历而言,不能使用递归来进行遍历。要使用队来进行。(类似于图的广度优先遍历)

以树T为例
先将A入队 队内元素A 遍历序列为空
将队首A出队,访问,入队A的左右子树,队内元素BC,遍历序列A
将队首B出队,访问,入队B的左右子树,队内元素CD,遍历序列AB
将队首C出队,访问,入队C的左右子树,队内元素DEF,遍历序列ABC
将队首D出队,访问,入队D的左右子树, 队内元素EFG,遍历序列ABCD
将队首E出队,访问,入队E的左右子树, 队内元素FG,遍历序列ABCDE
将队首F出队,访问,入队F的左右子树, 队内元素G,遍历序列ABCDE
将队首G出队,访问,入队G的左右子树, 队内元素空,遍历序列ABCDEFG
队空,结束

void levelOrder(BiTree root){
	BiTree p=root;
	CSeQeue *S;
	S=InitSeQueue();//创建队 
	InSeQueue(S,p);//入队 
	while(!EmptySeQueue(S)){
		QutSeQueue(S,&p);//出队 
		printf("%c",p->data);
		if(p->lChild!=NULL)
			InSeQueue(S,p->lChild);//入队 
		if(p->rChild!=NULL)
			InSeQueue(S,p->rChild);//入队 
	}
}

三.二叉树的遍历 栈实现

1.前序遍历

对于前序遍历而言,使用栈来实现,和递归很像
规则,

  • 先访问根节点,然后将根节点入栈
  • 遍历左子树。遍历完之后,
  • 出栈,获得右子树指针,遍历它的右子树

以树T为例:(可以自己根据代码走一遍)
访问A节点,入栈,栈内元素A,遍历序列A
访问A节点的左子树B,B入栈,栈内元素AB,遍历序列AB
访问B节点的左子树D,D入栈,栈内元素ABD,遍历序列ABD
访问D节点的左子树,左子树为空,左子树的访问完毕,出栈栈顶D,栈内元素AB,遍历序列为ABD
访问D节点的右子树G,G入栈,栈内元素ABG,遍历序列ABDG
访问G节点的左子树,左子树为空,出栈栈顶G,栈内元素AB,遍历序列ABDG
访问G节点的右子树,右子树为空,出栈栈顶B,栈顶元素A,遍历序列为ABDG
访问B节点的右子树,右子树为空,出栈栈顶A,栈内元素空,遍历序列ABDG
访问A节点的右子树C,C入栈,栈内元素C,遍历序列ABDGC
……
最后当需要出栈是,栈为空,则表示遍历完成

void PreOrder(BiTree root){
	BiTree p;
	SeqStack *S;
	S = InitStack();
	p = root;
	while(p!=NULL||StackEmpty(S)==0){
		
		while(p!=NULL){     
			printf("%c",p->data); 
			Push(S,p); //入栈 
			p=p->lChild; //遍历左子树 
		}
		if(StackEmpty(S)==0){
			Pop(S,&p);//出栈 
			p=p->rChild; //遍历右子树 
		} 
		
	}	
}
2.中序遍历

和前序遍历类似。

  • 先入栈,遍历它的左子树 左子树遍历完之后
  • 出栈,访问根节点,
  • 遍历右子树
void InOrder(BiTree root){
	BiTree p;
	SeqStack *S;
	S = InitStack();
	p = root;
	while(p!=NULL||StackEmpty(S)==0){
		
		while(p!=NULL){     
			Push(S,p); //入栈 
			p=p->lChild; //遍历左子树 
		}
		if(StackEmpty(S)==0){
			Pop(S,&p);//出栈 
			printf("%c",p->data); 
			p=p->rChild; //遍历右子树 
		} 
		
	}	
}
3.后序遍历

和前序中序遍历不同,后序遍历稍微麻烦些,因为涉及到两次访问栈顶

  • 先入栈,,遍历左子树,左子树遍历完之后
  • 访问栈顶(不出栈) 得到右子树指针,遍历右子树,右子树遍历完之后
  • 出栈栈顶元素,访问根节点
void PostOrder(BiTree root){
	BiTree p,q;
	SeqStack *S;
	S = InitStack();
	q = NULL;
	p = root;
	while(p!=NULL||StackEmpty(S)==0){
		while(p!=NULL){ //出循环时p左子树为空 
			Push(S,p); //入栈  
			p=p->lChild; //遍历左子树 
		}
		if(StackEmpty(S)==0){
			GetTop(S,&p);
			if(p->rChild==NULL||p->rChild==q){ //如果栈顶元素p右子树为空,或者上一步输出的是他的右子树 
				Pop(S,&p);
				printf("%c",p->data);
				q=p;
				p=NULL; 
			}else{ //如果上一步访问是栈顶元素的左子树 
				p=p->rChild;
			}
		}
	
	}
	
} 

四.二叉树的建立

单独一个遍历序列是无法唯一确定一颗二叉树的,想要根据遍历序列确定一颗二叉树,只有两种方法,一种是根据扩展的遍历序列,一种是根据中序遍历序号和前序(中序)遍历序列。所以我们二叉树的创建就是根据这两种思路来进行的。 我们以扩展的先序遍历序列和先序+中序遍历序列为例创建二叉树

1.根据扩展的先序序列创建二叉树.

a. 扩展的先序序列
扩展的先序序列,就是将二叉树节点的左右空子树也用特殊符号表示出来。
比如树T的扩展序列为ABDG^CEF^
b. 代码

BiTree CreatBiTree(BiTree root){  //二叉树的建立(由扩展的先序序列建立的二叉树) 
	static int count;
	char ch=str[count];
	count++;
	if(ch=='#')
		return NULL;
		
	root = (BiTNode *)malloc(sizeof(BiTNode));
	root->data = ch;
	root->lChild = CreatBiTree(root->lChild);//以当前节点的左指针为下一级二叉树的跟 
	root->rChild = CreatBiTree(root->rChild);//以当前节点的右指针为下一级二叉树的跟	
	return root;
}
2.根据先序和中序序列创建二叉树

思路,根据先序序列确定根,根据中序序列确定根节点的左右子树。然后根据先序序列确定左右子树的根节点,根据中序序列确定左右子树的左右子树…

BiTree CreatBiTree(char *frontArray,char *centreArray,int n){
	BiTree root;
	char lfArray[N],rfArray[N];
	char lcArray[N],rcArray[N];
	int ln,rn,i,j;
	char ch;
	if(n==0)
		return NULL;
		
	ch = frontArray[0];
	root = (BiTNode *)malloc(sizeof(BiTNode));
	root->data = ch;

	for(i=0;centreArray[i]!=ch;i++){        //左子树的后序 
		lcArray[i] = centreArray[i];
		
	}
	ln=i;
	i++;
	for(rn=0;i<n;rn++,i++){               //右子树的后序				          
		rcArray[rn] = centreArray[i];
	}
	
	for(i=0;i<ln;i++){                   //左子树的先序 
		lfArray[i] = frontArray[i+1];
	}
	
	for(j=0;j<rn;j++,i++){              //右子树的先序 
		rfArray[j]=frontArray[i+1];
	}  
	
	                           
	root->lChild = CreatBiTree(lfArray,lcArray,ln);//以当前节点的左指针为下一级二叉树的跟 
	root->rChild = CreatBiTree(rfArray,rcArray,rn);//以当前节点的右指针为下一级二叉树的跟	
	return root;
}

五.二叉树的遍历的应用

1. 节点及所在层次

这里写图片描述

void PreOrder(BiTree root){    //先序遍历输出加层次 
	int static count;
	if(root==NULL)
		return;
	count++;	
	printf("(%c,%d)",root->data,count);
	PreOrder(root->lChild);
	PreOrder(root->rChild);
	count--;
}
2. 某层叶子节点个数

这里写图片描述

int f1(BiTree root,int k){   
	int static countlevel;
	int static countleaf;
	if(root==NULL)
		return;
	countlevel++;	
	if(countlevel == k&&root->lChild==NULL&&root->rChild==NULL)
		countleaf++;
	f1(root->lChild,k);
	f1(root->rChild,k);
	countlevel--;
	return countleaf;
}
3. 交换左右子树

这里写图片描述

void exchange(BiTree root){     //交换各节点的左右子树 
	BiTree t;
	if(root==NULL)
		return;
	t=root->lChild;
	root->lChild=root->rChild;
	root->rChild=t;
	exchange(root->lChild);
	exchange(root->rChild);
}
4. 根节点到叶子节点的路径

这里写图片描述

void PreOrder(BiTree root){     
	int static count;
	
	int i;
	if(root==NULL)
		return;
	count++;	
	if(root->lChild==NULL&&root->rChild==NULL){
		printf("%c:",root->data);
		
		for(i=1;i<count;i++){
			printf("%c",array[i]);
		}
			
		printf("\n");
	}else{
		array[count]=root->data;
	}
	PreOrder(root->lChild);
	PreOrder(root->rChild);
	count--;
}
5. 两个节点的共同祖先

这里写图片描述

int  count;	
int  flag; //标记是否找到 
	
void PreOrder(BiTree root,char ch,char *array){  
	
	if(root==NULL)
		return ;
		
	count++;	
	if(root->data==ch){
		flag = 1;
		array[count]=0;
		return ;
	}else if(flag ==0&&root!=NULL){
		array[count]=root->data;	
	}
	
	
	
	if(flag==0){
		PreOrder(root->lChild,ch,array);
		PreOrder(root->rChild,ch,array);
	}
	
	count--;
	return;
}

六.源码地址

在text12中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值