关于二叉树的一系列示例代码(更新中)

一、建立按照层次编号的二叉树

//按照层次编号的二叉树

/* 
1  
2 3 
4 5 6 7
8 9 10 11 12 13 14 15 
其中 1的子树是2和3
2的子树是4和5,3的子树是6和7
以此类推 
*/

//按照层次编号的二叉树,在遍历和构造时要善于利用队列这一数据结构 
 
#include <stdio.h>
#include <stdlib.h>
#define QUEUENUM 1000

typedef struct node
{
	int id;
	//编号 
	struct node* left;
	//指向左孩子的指针 
	struct node* right;
	//指向右孩子的指针 
	struct node* father;
	//指向父亲的指针 
	
}Tree;

void ini_tree(Tree** proot,int n);
//用于构建树,第一个参数是指向被构建树的根指针的指针,第二个参数是被构建树的结点个数 
void read_tree(Tree* root);
//用于按照层次遍历树,参数是被遍历树的根指针 

int main(void)
{
	int n;
	scanf("%d",&n);
	//n用来记录结点的个数
	
	Tree* root=NULL;
	//root为根指针 
	ini_tree(&root,n);
	read_tree(root); 
	
	return 0;
} 

void ini_tree(Tree** proot,int n)
{
	Tree **queue;
	Tree **queue_father;
	queue=(Tree**)calloc(QUEUENUM,sizeof(Tree*));
	queue_father=(Tree**)calloc(QUEUENUM,sizeof(Tree*));
	int front=-1;
	int rear=-1;
	//以上代码块构建了一个复合队列
	//队列存储的信息是,下一个结点应该构建在哪里?以及,被构建结点的父亲是谁?
	//queue[i]的值是某个待构建结点的地址 
	//queue_father[i]的值是 这个待构建结点父亲的地址 
	
	Tree* c;
	//用于被calloc的创建结点打工人 
	
	int i;
	for (i=1;i<=n;i++)
	{
		if (*proot==NULL)
		{
			c=(Tree*)calloc(1,sizeof(Tree));
			c->id=i;
			c->left=NULL;
			c->right=NULL;
			c->father=NULL;
			*proot=c;
			
			queue[++rear]=c->left;
			queue_father[rear]=c;
			queue[++rear]=c->right; 
			queue_father[rear]=c;
			//构建结点的同时,把可以构建结点的地址按顺序压入队列中 
		}
		else
		{
			++front;
			c=(Tree*)calloc(1,sizeof(Tree));
			c->id=i;
			c->left=NULL;
			c->right=NULL;
			c->father=queue_father[front];
			
			//注意,父亲结点的左右孩子地址需要被赋值
			//一个父亲要先有左孩子,再右孩子
			//如果左孩子的地址为空,那么这个父亲就没有左孩子
			//我们就把新建立的结点分给这个父亲当他的左孩子 
			if (c->father->left==NULL)
			{
				c->father->left=c;
			}
			//如果左孩子的地址为空,那么这个父亲就有左孩子
			//但按照算法的流程我们知道,这个父亲肯定没有右孩子 
			//我们就把新建立的结点分给这个父亲当他的右孩子 
			else
			{
				c->father->right=c;
			} 
			
			queue[++rear]=c->left;
			queue_father[rear]=c;
			queue[++rear]=c->right; 
			queue_father[rear]=c;
			//构建结点的同时,把可以构建结点的地址按顺序压入队列中 	
		}
	}
	
	free(queue);
	free(queue_father);
}

void read_tree(Tree* root)
{
	Tree **queue;
	queue=(Tree**)calloc(QUEUENUM,sizeof(Tree*));
	int front=-1;
	int rear=-1;
	//以上代码块构建了一个队列
	//队列存储的信息是,下一个遍历的结点在哪里?
	//queue[front]的值是下一个待遍历结点的地址
	
	Tree* p=root;
	printf("id=%d\n",p->id);		
	if (p->left!=NULL)
	queue[++rear]=p->left;
	if (p->right!=NULL)
	queue[++rear]=p->right;
	
	while(front<rear)
	{
		p=queue[++front];
		printf("id=%d\n",p->id);
		if (p->left!=NULL)
		queue[++rear]=p->left;
		if (p->right!=NULL)			
		queue[++rear]=p->right;
	}
	
	free(queue);
}
 
 

注意:

1、为什么我在函数ini_tree和read_tree中要采用calloc的方式构建队列数组?

因为c语言在子函数中所能直接建立数组的大小是有限的。我们构建队列时采取了最为简单的非循环顺序储存结构,需要大量的空间。所以需要用calloc在申请空间

2、为什么ini_tree的参数是二重指针,而read_tree的参数是一重指针?

因为ini_tree函数需要对root指针的值进行改变,且作用于全局。root是一个一重指针,在函数中需要借助二重指针进行赋值。

二、构建查找二叉树——非递归实现

#include <stdio.h>
#include <stdlib.h>
#define NUM 100
#define QUEUENUM 1000

typedef struct node
{
	int num;
	int count;
	//数据 
	struct node* left;
	//指向左孩子的指针
	struct node* right;
	//指向右孩子的指针
	struct node* father;
	//指向父亲的指针 
	
}Tree;

Tree* stack[NUM];
//一个指针栈,存储着当前遍历结点到根结点的所有指针
//其中,stack[0]恒为根结点的指针,以此类推 
int top=-1;

void read_tree(Tree* root);
//用于按照层次遍历树,参数是被遍历树的根指针 
void build_tree(Tree** proot,int num);
//用于构建查找树,每次处理一个数据 

int main(void)
{
	int n;
	scanf("%d",&n);
	
	Tree* root;
	
	int i;
	int num;
	for (i=1;i<=n;i++)
	{
		scanf("%d",&num);
		build_tree(&root,num);
	}
	
	read_tree(root);

	return 0;
}

void build_tree(Tree** proot,int num)
{
	Tree* c;
	//用于被calloc的创建结点打工人  
	Tree* tmp;
	//用于寄存地址的一个指针 
	top=-1;
	//初始化栈 
	
	if (*proot==NULL)
	{
		c=(Tree*)calloc(1,sizeof(Tree));
		c->left=NULL;
		c->right=NULL;
		c->num=num;
		c->count=1;
		*proot=c;
		//如果头指针为空,不用考虑那么多,直接建立根结点 
	}
	else
	{
		stack[++top]=*proot;
		//如果头指针不空,那么就按照正常流程进行,先把头指针压入栈中 
		while(1)
		{
			//遍历到任何一个结点时,要考虑待进树的数据和当前结点数据的大小
			//如果当前结点数据和待进树的数据相等,直接进树,解决战斗,退出循环 
			if (num==stack[top]->num)
			{
				(stack[top]->count)++;
				break;
			} 
			//如果进树的数据小于当前结点数据,肯定是去左边,那就要看看左子树是不是空了 
			else if (num<stack[top]->num)
			{
				//左子树为空,直接创建左子树,然后完成任务,退出循环 
				if (stack[top]->left==NULL)
				{
					c=(Tree*)calloc(1,sizeof(Tree));
		            c->left=NULL;
		            c->right=NULL;
		            c->num=num;
		            c->count=1;
		            stack[top]->left=c;
		            break;
				}
				//否则,往下继续探索
				//注意,这时候要把下一个要探索的结点的地址压入栈中 
				else
				{
					tmp=stack[top]->left;
				    stack[++top]=tmp;
				    continue;
				}
			}
			//否则,那么进树的数据就大于当前结点数据,要去右边了,同样要看看右子树是不是空了
			else
			{
				//右子树为空,直接创建右子树,然后完成任务,退出循环 
				if (stack[top]->right==NULL)
				{
					c=(Tree*)calloc(1,sizeof(Tree));
		            c->left=NULL;
		            c->right=NULL;
		            c->num=num;
		            c->count=1;
		            stack[top]->right=c;
		            break;
				}
				//否则,往下继续探索
				//注意,这时候要把下一个要探索的结点的地址压入栈中
				else
				{
					tmp=stack[top]->right;
				    stack[++top]=tmp;
				    continue;
				}
			}
	    }
	}
}

//这是第一个示例程序中写的按层次遍历的代码,这里直接拿来用了
//printf语句改了一下 
void read_tree(Tree* root)
{
	Tree **queue;
	queue=(Tree**)calloc(QUEUENUM,sizeof(Tree*));
	int front=-1;
	int rear=-1;
	
	Tree* p=root;
	printf("num=%d count=%d\n",p->num,p->count);		
	if (p->left!=NULL)
	queue[++rear]=p->left;
	if (p->right!=NULL)
	queue[++rear]=p->right;
	
	while(front<rear)
	{
		p=queue[++front];
		printf("num=%d count=%d\n",p->num,p->count);
		if (p->left!=NULL)
		queue[++rear]=p->left;
		if (p->right!=NULL)			
		queue[++rear]=p->right;
	}
	
	free(queue);
}

三、二叉树的遍历——非递归实现

#include <stdio.h>
#include <stdlib.h>
#define NUM 100

typedef struct node
{
	int num;
	int count; 
	struct node* left;
	struct node* right;
	struct node* father;
	
}Tree;

Tree* stack[NUM];
int isread_stack[NUM];
int top=-1;

void build_tree(Tree** proot,int num);
void read_tree1(Tree* root);

int main(void)
{
	int n;
	scanf("%d",&n);
	
	Tree* root;
	
	int i;
	int num;
	for (i=1;i<=n;i++)
	{
		scanf("%d",&num);
		build_tree(&root,num);
	}
	
	read_tree1(root);
	 
	return 0;
}

void build_tree(Tree** proot,int num)
{
	Tree* c;
	Tree* tmp;
	top=-1;
	
	if (*proot==NULL)
	{
		c=(Tree*)calloc(1,sizeof(Tree));
		c->left=NULL;
		c->right=NULL;
		c->num=num;
		c->count=1;
		*proot=c;
	}
	else
	{
		stack[++top]=*proot;
		while(1)
		{
			if (num==stack[top]->num)
			{
				(stack[top]->count)++;
				break;
			} 
			else if (num<stack[top]->num)
			{
				if (stack[top]->left==NULL)
				{
					c=(Tree*)calloc(1,sizeof(Tree));
		            c->left=NULL;
		            c->right=NULL;
		            c->num=num;
		            c->count=1;
		            stack[top]->left=c;
		            break;
				}
				else
				{
					tmp=stack[top]->left;
				    stack[++top]=tmp;
				    continue;
				}
			}
			else
			{
				if (stack[top]->right==NULL)
				{
					c=(Tree*)calloc(1,sizeof(Tree));
		            c->left=NULL;
		            c->right=NULL;
		            c->num=num;
		            c->count=1;
		            stack[top]->right=c;
		            break;
				}
				else
				{
					tmp=stack[top]->right;
				    stack[++top]=tmp;
				    continue;
				}
			}
	    }
	}
}

void read_tree1(Tree* root)
{
	top=-1;
	stack[++top]=root;
	isread_stack[top]=0;
	//栈初始化,把根结点的地址和被访问状态信息压入两个栈中 
	
	Tree* tmp;
	//一个临时变量指针,在这里显得突兀,其实不重要,先别管它 
	
	while(1)
	{
		//如果是第一次到达 
		if (isread_stack[top]==0)
		{
			printf("num=%d count=%d\n",stack[top]->num,stack[top]->count);
			isread_stack[top]=1;
			
			//遍历左子树
		    if (stack[top]->left!=NULL)
		    {
			    tmp=stack[top]->left;
			    stack[++top]=tmp;
			    continue;
		    }
		}
		//如果是第二次到达 
		else if (isread_stack[top]==1)
		{
			isread_stack[top]=2;
			
			//遍历右子树
		    if (stack[top]->right!=NULL)
		    {
			    tmp=stack[top]->right;
			    stack[++top]=tmp;
				continue;
		    } 
		}
		//如果是第三次到达 
		else if (isread_stack[top]==2&&top!=0)
		{
			isread_stack[top]=0;
			top--;
		}
		//如果是第三次到达且这个结点是根结点
		//意味着树遍历完了,函数该结束了,退出循环吧 
		else if (isread_stack[top]==2&&top==0)
		{
			break;
		}
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值