C语言实现数据结构代码(三)-树与二叉树-二叉树-二叉树的应用

目录

一、遍历模板

1、先序遍历模板

2、中序遍历模板

3、后序遍历模板

二、例题

 1、表达式(a-(b+c))*(d/e)存储在图6-7所示的一棵以二叉链表为存储结构的二叉树中(二叉树结点的data域为字符型),编写程序求出该表达式的值(表达式中的操作数都是一位的整数)。

2、写一个算法求一棵二叉树的深度,二叉树以二叉链表为存储方式。

3、在一棵以二叉链表为存储结构的二叉树中,查找data域值等于key 的结点是否存在(找到任何一个满足要求的结点即可),如果存在,则将q指向该结点,否则q赋值为NULL,假设data为int型。

4、假设二叉树采用二叉链表存储结构存储,编写一个程序,输出先序遍历序列中第k 个结点的值,假设k 不大于总的结点数(结点data域类型为char型)。


一、遍历模板

1、先序遍历模板

//1、先序遍历
void preOrder(BTNode *p)
{
	if(p!=NULL){
		visit(p->data);
		preOrder(p->lchild);
		preOrder(p->rchild);
	}
} 

2、中序遍历模板

//2、中序遍历
void inOrder(BTNode *p)
{
	if(p!=NULL){
		preOrder(p->lchild);
		visit(p->data);
		preOrder(p->rchild);
	}
} 

3、后序遍历模板

//3、后序遍历
void postOrder(BTNode *p)
{
	if(p!=NULL){
		preOrder(p->lchild);
		preOrder(p->rchild);
		visit(p->data);
	}
} 

二、例题

 1、表达式(a-(b+c))*(d/e)存储在图6-7所示的一棵以二叉链表为存储结构的二叉树中(二叉树结点的data域为字符型),编写程序求出该表达式的值(表达式中的操作数都是一位的整数)。

C语言实现数据结构代码(三)-树与二叉树-二叉树-二叉树的应用_第1张图片

/*
 [分析]
 	1、先分别算出左子树a、右子树的值b
	2、很结点存储的是运算符*,将 a*b的结果返回
    3、此处用后序遍历
*/ 
int comp(BTNode *p){
	int A,B;
	if(p!=null){
		if(p->lchild!=NULL && p->rchild!=NULL){		//p要么有左右孩子,要么没有,没有左右孩子结点时,它是根节点,也就是最终计算的值 
			A=comp(p->lchild);
			B=comp(p->rchild);
			return op(A,B,p->data);
		}else{
			return p->data+'0';		//注意此处的写法,将字符转为数字 
		} 	
	}else{
		return 0;
	} 
}

2、写一个算法求一棵二叉树的深度,二叉树以二叉链表为存储方式。

/*
 [分析]
 	1、一棵树,左子树的深度为LD,右子树的深度为RD,那么这棵树的深度就为max{LD,RD}+1 
 	2、用递归 ,注意递归结束条件。
	 			如本题,递归结束条件就是遍历到叶子节点,叶子节点的孩子传入递归函数,传入参数为空,此时直接返回0(叶子节点的子树的深度为0) 
	
*/ 
int getDepth(BTNode *p){
	int LD,LR;
	if(p!=null){
		LD=getDepth(p->lchild);
		LR=getDepth(p->rcild);
		return LD>LR ? LD+1 : LR+1;
	}else{
		return 0;
	} 
}

3、在一棵以二叉链表为存储结构的二叉树中,查找data域值等于key 的结点是否存在(找到任何一个满足要求的结点即可),如果存在,则将q指向该结点,否则q赋值为NULL,假设data为int型。

/*
 [分析]
 	遍历二叉树,此处选择先序遍历 
*/ 

void search(BTNode *p,BTNode *&q, int key){
	if(p!=NULL) {
		if(p->data == key){
			q = p;
		}else{
			serach(p->lchild,q,key);
			search(p->rchild,q,key);
		}
	}	
} 

//改进
 void search(BTNode *p,BTNode *&q, int key){
	if(p!=NULL) {
		if(p->data == key){
			q = p;
		}else{					
			serach(p->lchild,q,key);
			if(q==NULL)                    //加了这一行 
				search(p->rchild,q,key);
		}
	}	
} 

4、假设二叉树采用二叉链表存储结构存储,编写一个程序,输出先序遍历序列中第k 个结点的值,假设k 不大于总的结点数(结点data域类型为char型)。

/*
 [分析]
 	遍历二叉树,此处选择先序遍历 。灵活运用遍历模板
*/ 

int n=0;
void getValue(BTNode *p,int k){
	if(p!=NULL){
		++n;                        //注意++n的位置,先加n,再与k比较
		if(k==n)
		{
			cout<data<child,k);
		getValue(p->rchild,k); 
	}
} 

//将题目改成中序
int n=0;
void getValueIn(BTNode *p,int k){
	if(p!=NULL){
		getValueIn(p->lchild,k);
		
		n++;
		if(k==n){
			cout<data<rchild,k);
	}
} 

//将题目改成后序 
int n=0;
void getValuePost(BTNode *p,int k){
	if(p!=NULL){
		getValuePost(p->lchild,k);
		getValuePost(p->rchild,k);
		
		n++;
		if(k==n){
			cout<data<

三、层次遍历

1、模板

图6-10所示为二叉树的层次遍历,即按照箭头所指方向,按照1、2、3、4的层次顺序,对二叉树中各个结点进行访问(此图反映的是自左至右的层次遍历,自右至左的方式类似)。

C语言实现数据结构代码(三)-树与二叉树-二叉树-二叉树的应用_第2张图片

/*
 【分析】
 	要进行层次遍历,需要建立一个循环队列。
	 	先将二叉树头结点入队列,然后出队列,访问该结点,如果它有左子树,则将左子树的根结点入队;如果它有右子树,则将右子树的根结点入队。
		然后出队列,对出队结点访问。如此反复,直到队列为空为止。 (记住最后一句话的说法)
 【步骤】 
 		1、定义并初始化一个循环队列 
 		2、将二叉树的根节点入队
		3、开始遍历列队。
				出队并访问队头结点。
					队头结点有左孩子:队头结点的左孩子入队;
					堆头结点有右孩子:队头结点的右孩子入队; 		 
*/ 

void levelRecurse(BTNode *p){
	//1、定义并初始化一个循环队列
	int front,rear;
	BTNode *que[maxSize];  //maxSize为已定义好的变量,是队列的最大长度
	front=rear=0;			//初始化队列 
	BTNode *q; 
	
	if(p!=NULL){
		//根结点入队 
		rear = (rear+1)%maxSize;		//循环队列,入队出队时,千万别忘了取余MaxSize !!! 
		que[rear]=p;
		
		while(front!=rear){				//当队列不为空时进行循环 
			//出队头元素 
			front = (front+1)%maxSize; 
			q=que[front];
			 
			visit(q->data);				//访问队头元素
			
			if(q->lchild !=NULL){		//左孩子不为空
				//左孩子结点入队
				rear =(rear+1)%maxSize;
				que[rear]=q->lchild;				
			}
			
			if(q->rchild !=NULL){	//右孩子不为空
				//右孩子结点入队
				rear =(rear+1)%maxSize;
				que[rear]=q->rchild;				
			}
			 
		} 
		 
	} 
}

2、例题(层次遍历的应用)

假设二叉树采用二叉链表存储结构存储,设计一个算法,求出该二叉树的宽度(具有结点数最多的那一层上的结点个数)。

/*
 【分析】
 	前提:层次遍历的基础 
 	第一点: 出队元素的左、右孩子结点的层次 = 出队元素的层数+1;
		 	 根节点的层数为1;
 	第二点: 知道了即将入队的结点的层数,那么可以在结点入队时,一起将入队结点的层数存进去。 
	第三点: 将循环队列改成非循环队列(前提是队列长度大于二叉树的结点个数),
		 	 这样对二叉树的结点遍历结束之后,二叉树的结点都存在队列中。
		 	 
 【步骤】 
 		1、 
 			 
*/ 

typedef struct St		//定义队列中存储的元素的结构体,为结点+结点所在层数 
{
	BTNode *p;
	int levelNum;
}St;					//结构体定义,千万不要忘记最后的结尾!!! 

int getWidthOfTree(BTNode *b){
	//1、定义并初始化一个队列
	St que[maxSize];
	int front,rear;
	front=rear=0; 
	
	BTNode *q;
	int Lnum=0; //存放最大层数 
	
	if(b!=NULL){	 
		//根节点入队,层数为1 
		rear++;
		que[rear]->p=b;
		que[rear]->levelNum=1; 
		
		while(front!=rear){
			++front;
			q=que[front]->p;
			Lnum=que[front]->levelNum; 
			
			if(q->lchild!=NULL){	//出队结点的左孩子结点及其层数入队 
				++rear;
				que[rear]->p=q->lchild;
				que[rear]->levelNum=Lnum+1;
			}
			if(q->rchild!=NULL){			//出队结点的右孩子结点及其层数入队 
				++rear;
				que[rear]->p=q->rchild;
				que[rear]->levelNum=Lnum+1;
			}
		}//神来之笔!!!循环结束时,Lnum中存放的是二叉树的最大层数,方便后面遍历。
		
		int max=0;//变量max存放最大宽度
		int n; //变量n暂时存放每一层的结点个数 
		for(int i=1;i<=Lnum;++i){		//根据层数遍历 
			n=0; 
			for(int j=0;jlevelNum == i)
					n++;
				if(n>max)
					max=n;
			}
		}
		return max; 	
	}else{
		return 0;
	} 
} 

四、二叉树遍历算法的改进

1、先序遍历非递归算法

/*
	【分析】
		非递归,用到栈。
	【步骤】
		1、定义一个栈
		2、树的根结点入栈
		3、树的根结点出栈
		4、访问根结点
		5、根结点存在右孩子,右孩子入栈
		6、根结点存在左孩子,左孩子入栈
		7、如此反复,直到栈空 
*/
void preOrderNonRecursion(BTNode *bt){
	if(bt !=NULL){
		//1、定义并初始化一个栈,存放树节点 
		BTNode *Stack[maxSize];	 
		int top=-1;
		
		BTNode *p;		//定义p存放出栈结点 
		//2、根节点入栈 
		Stack[++top]=bt;
		while(top!=-1){
			p=Stack[top--];
			visit(p); 
			if(p->rchild!=NULL)			//注意:此处是右孩子先入栈,因为先入后出,先入右孩子,后访问右孩子 
				Stack[++top]=p->rchild;
			if(p->lchild!=NULL)
				Stack[++top]=p->lchild;
		} //循环结束,二叉树的遍历完成 
	}
}

2、中序遍历非递归算法

/*
	【分析】
		非递归,用到栈。
		
		结点出栈时访问结点,结点的右孩子是在访问出栈结点之后 
		
	【步骤】
		1、根结点入栈
		2、循环执行如下操作:如果栈顶结点左孩子存在,则左孩子入栈;
							 如果栈顶结点左孩子不存在,则出栈并输出栈顶结点,
								然后检查其右孩子是否存在,如果存在,则右孩子入栈。
		3、当栈空时,算法结束 
*/

void InOrderNonRecursion(BTNode *bt){
	if(bt!=NULL){
		BTNode *Stack[maxSize];
		int top=-1;
		
		BTNode *p;		//p存放的是即将进栈的结点 
		p=bt;
		
		while(top!=-1 || p!=NULL){	//在遍历的过程中,可能会出现栈空的情况,所以不能只靠栈空来结束循环, 
			while(p!=NULL){			//即将入栈的结点p,入栈,并将p指向入栈结点的左孩子 
				Stack[++top]=p;    //一路左到底,然后开始出栈访问操作
				p=p->lchild;
			}
			if(top!=-1){			//当p为空时,也就是栈顶元素没有左孩子,栈顶元素出栈,访问栈顶元素,再将p指向出栈元素的右孩子 
				p=Stack[top--];
				Visit(p);            //访问完出栈元素之后,将p(即将进站的元素)指向出栈元素的右孩子
				p=p->rchild;
			}
		}
	}
	
} 

3、后序遍历非递归算法

先序遍历:根左右
后序遍历:左右根

逆后续遍历:根右左

后续非递归遍历就是:先对树进行根右左的遍历,然后将根左右的顺序放入到一个栈2中,再对栈2进行出栈遍历操作。

你可能感兴趣的:(数据结构,C语言,数据结构)