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域为字符型),编写程序求出该表达式的值(表达式中的操作数都是一位的整数)。

/*
 [分析]
 	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<<p->data<<endl;	//1、打印 ,语法 
			return;					//2、结束遍历 
		}
		getValue(p->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<<p->data<<endl;
			return;				//找到后别忘了结束递归 
		}
			
		getValueIn(p->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<<p->data<<endl;
			return;				
		}							
	}
} 

三、层次遍历

1、模板

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

/*
 【分析】
 	要进行层次遍历,需要建立一个循环队列。
	 	先将二叉树头结点入队列,然后出队列,访问该结点,如果它有左子树,则将左子树的根结点入队;如果它有右子树,则将右子树的根结点入队。
		然后出队列,对出队结点访问。如此反复,直到队列为空为止。 (记住最后一句话的说法)
 【步骤】 
 		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;j<rear;++j){	//遍历存放结点的队列 
				if(que[j]->levelNum == 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进行出栈遍历操作。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
平衡二叉树是一种特殊的二叉树,它的左右子的高度差不超过1。AVL是一种自平衡的二叉搜索,它的高度始终保持在O(log n)。 下面是C语言实现平衡二叉树(AVL)的代码: ``` #include <stdio.h> #include <stdlib.h> /* 定义平衡二叉树节点结构体 */ struct AVLNode { int data; // 存储的数据 int height; // 节点高度 struct AVLNode *leftChild; // 左子 struct AVLNode *rightChild; // 右子 }; /* 获取节点高度 */ int getHeight(struct AVLNode *node) { if (node == NULL) { return -1; } else { return node->height; } } /* 获取节点平衡因子 */ int getBalanceFactor(struct AVLNode *node) { if (node == NULL) { return 0; } else { return getHeight(node->leftChild) - getHeight(node->rightChild); } } /* 更新节点高度 */ void updateHeight(struct AVLNode *node) { node->height = 1 + (getHeight(node->leftChild) > getHeight(node->rightChild) ? getHeight(node->leftChild) : getHeight(node->rightChild)); } /* 右旋操作 */ struct AVLNode *rotateRight(struct AVLNode *node) { struct AVLNode *newRoot = node->leftChild; node->leftChild = newRoot->rightChild; newRoot->rightChild = node; updateHeight(node); updateHeight(newRoot); return newRoot; } /* 左旋操作 */ struct AVLNode *rotateLeft(struct AVLNode *node) { struct AVLNode *newRoot = node->rightChild; node->rightChild = newRoot->leftChild; newRoot->leftChild = node; updateHeight(node); updateHeight(newRoot); return newRoot; } /* 插入操作 */ struct AVLNode *insert(struct AVLNode *root, int data) { if (root == NULL) { root = (struct AVLNode *) malloc(sizeof(struct AVLNode)); root->data = data; root->height = 0; root->leftChild = NULL; root->rightChild = NULL; } else if (data < root->data) { root->leftChild = insert(root->leftChild, data); if (getHeight(root->leftChild) - getHeight(root->rightChild) == 2) { if (data < root->leftChild->data) { root = rotateRight(root); } else { root->leftChild = rotateLeft(root->leftChild); root = rotateRight(root); } } } else if (data > root->data) { root->rightChild = insert(root->rightChild, data); if (getHeight(root->rightChild) - getHeight(root->leftChild) == 2) { if (data > root->rightChild->data) { root = rotateLeft(root); } else { root->rightChild = rotateRight(root->rightChild); root = rotateLeft(root); } } } updateHeight(root); return root; } /* 中序遍历 */ void inOrderTraversal(struct AVLNode *root) { if (root != NULL) { inOrderTraversal(root->leftChild); printf("%d ", root->data); inOrderTraversal(root->rightChild); } } int main() { struct AVLNode *root = NULL; int data[] = {5, 2, 8, 1, 3, 6, 9}; int len = sizeof(data) / sizeof(data[0]); int i; for (i = 0; i < len; i++) { root = insert(root, data[i]); } inOrderTraversal(root); return 0; } ``` 以上代码实现了平衡二叉树的插入和中序遍历操作。在插入操作中,根据插入节点的值和当前节点的值的大小关系,不断递归向左或向右子进行插入操作,并在递归返回时更新节点高度和进行平衡操作。在平衡操作中,根据节点的平衡因子进行旋转操作,使重新平衡。在中序遍历操作中,按照左子、根节点、右子的顺序遍历中的节点,输出节点的值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值