C语言数据结构之二叉树的层次建树及遍历方法(前序,中序,后序,层次遍历)

C语言数据结构之二叉树的层次建树及遍历方法(前序,中序,后序,层次遍历)

tips:前些天学习了C语言数据结构链表,栈,队列。今天来学习一下C语言数据结构之二叉树的各种操作。

注意:二叉树的层次建树是建立的完全二叉树。


完全二叉树:对满二叉树进行编号,约定编号从1开始,从上到下,从左至右,每个结点的编号与其深度相同的满二叉树结点的编号一一对应。


完全二叉树的性质:

  • 具有n个结点的完全二叉树的深度为 ⌊ log 2 n \log_2n ⌋+1;

  • 若编号为i的结点左右子树存在,则其左子树的编号为2i,右子树编号为2i+1;

  • 编号从1开始,长度为n的完全二叉树最后一个父结点编号为⌊n/2⌋;
    编号从0开始,长度为n的完全二叉树最后一个父结点编号为⌊n/2⌋-1;


一、二叉树的层次建树(完全二叉树)

二叉树结构体:

//二叉树结构体
#define N 11 //0不存放数值,这里存10个元素,长度为11
typedef struct node
{
	char c;//结点的数据,这里为了方便定义char
	struct node *left;//左指针
	struct node *right;//右指针
}Node,*pNode;

层次遍历需要用到的队列:

typedef struct LinkList
{
	pNode p;//将树的结点作为队列的值域
	struct LinkList *pNext;
}LinkList,*pLinkList;

//队列
typedef struct Queue
{
	pLinkList  front, rear;//队头队尾指针
}Queue,*pQueue;

队列的操作前面已经学习过,这里就不展开了(注意:这里的值域是个结点)。

这里需要用到队列操作:

扫描二维码关注公众号,回复: 11604833 查看本文章
//初始化队列
void InitQueue(pQueue que);

//入队
void EnQueue(pQueue que, pNode p);

//出队
void DeQueue(pQueue que);

//访问队头元素
pNode getFront(pQueue que);

//判断队列是否为空
int QueueEmpty(pQueue que);

1、二叉树的初始化

思路:

  • 将树的每个结点循环初始化;
  • 将树结点的值域初始化对应的值;
  • 将树指针域初始化为空;

具体实现:

//树的初始化
void InitBiTree(pNode *p,char *c)//c[]存放数据
{
	int i;
	//对树的结点初始化,0号结点也要初始化,可以不用它
	for (i = 0; i < N; i++)
	{
		p[i] = (pNode)malloc(sizeof(Node));
		p[i]->c = c[i];//结点的值域初始化
		p[i]->left = NULL;
		p[i]->right = NULL;
	}
}

2、二叉树的层次建树

思路:

  • 将编号为1的树结点作为根结点;
  • 循环,为将要进树的除根结点以外的结点寻找合适位置;
  • 若结点左子树为空,则插入左子树;
  • 若结点左子树非空,右子树为空,则插入右子树;
  • 若结点左右子树均不为空,说明此结点已满,寻找下个结点;

具体实现:

//层次建树,参数是树的结点
void CreateBitree(pNode *p)
{
	int i, j;

	//开始层次建树,编号为1的结点作为根结点,找位置插入树的结点编号从2开始
	for (i = 2; i < N; i++)//进树的元素
	{
		for (j = 1; j < N; j++)//为进树的元素找合适的位置插入
		{
			if (p[j]->left == NULL)
			{
				p[j]->left = p[i];//左子树为空,插入左子树
				break;
			}
			if (p[j]->right == NULL)
			{
				p[j]->right = p[i];//左子树为空,插入左子树
				break;
			}
		}
	}
}

实现效果:
在这里插入图片描述


二、二叉树的遍历

1、二叉树前序遍历(根左右)

思路:

  • 递归实现遍历;
  • 先访问根结点,再访问左子树,再访问右子树

具体实现:

//树的前序遍历(根左右)
void preorder(pNode tree)//传根结点作为参数
{
	//判断树是否为空
	if (tree != NULL)
	{
		printf("%c", tree->c);
		preorder(tree->left);
		preorder(tree->right);
	}
}

2、二叉树中序遍历(左根右)

思路:

  • 递归实现遍历;
  • 先访问左子树,再访问根结点,再访问右子树;

具体实现:

//树的中序遍历(左根右)
void midorder(pNode tree)
{
	if (tree != NULL)
	{
		midorder(tree->left);
		printf("%c", tree->c);
		midorder(tree->right);
	}

}

3、二叉树后序遍历(左右根)

思路:

  • 递归实现遍历;
  • 先访问左子树,再访问右子树,再访根结点;

具体实现:

//树的后序遍历(左右根)
void lastorder(pNode tree)
{
	if (tree != NULL)
	{
		lastorder(tree->left);
		lastorder(tree->right);
		printf("%c", tree->c);
	}
}

4、二叉树层次遍历(借助队列)

思路:

  • 初始时,将根结点入队,并访问根结点;
  • 若有左子树,则将左子树的根结点入队;
  • 若有右子树,则将右子树的根结点入队;
  • 将根结点出队;
  • 重复上述步骤,直到队列为空;

具体实现:

//树的层次遍历
void levelorder(pNode tree)
{
	Queue queue;//定义一个队列
	pQueue que = &queue;

	InitQueue(que);

	EnQueue(que,tree);//根结点入队

	while (QueueEmpty(que)!= 1)//不为空
	{
		printf("%c",getFront(que)->c);//访问输出队首结点	

		//当队首结点左右子树存在时,将其左右子树结点入队
		if (getFront(que)->left!=NULL)
		{
			EnQueue(que, getFront(que)->left);//左子树入队
		}
		if (getFront(que)->right != NULL)
		{
			EnQueue(que, getFront(que)->right);//右子树入队
		}

		DeQueue(que);//队首元素出队
	}

}


至此,完全二叉树的创建以及遍历过程均已实现,接下来在main()函数中测试一下:

int main()
{
	//准备建树的元素,0号下标不存元素
	char c[N] = { ' ','A','B','C','D','E','F','G','H','I','J' };

	pNode p[N];//树的结点
	pNode tree;//根结点
	InitBiTree(p, c);//初始化树
	
	CreateBitree(p);//建树
	tree = p[1];//1号结点作为根结点

	//前序打印树(根左右)
	printf("前序遍历:");
	preorder(tree);
	printf("\n");

	//中序打印树(左根右)
	printf("中序遍历:");
	midorder(tree);
	printf("\n");

	//后序打印树(左右根)
	printf("后序遍历:");
	lastorder(tree);
	printf("\n");

	//层次遍历树
	printf("层次遍历:");
	levelorder(tree);
	printf("\n");

	return 0;
}

测试结果:
在这里插入图片描述


tips:成功不是将来才有的,而是从决定去做的那一刻起,持续累积而成。

猜你喜欢

转载自blog.csdn.net/wrlovesmile/article/details/108248228