DS--二叉树的创建和七种遍历方式,包括三种递归的前中后序遍历和三种非递归的前中后序遍历以及层次遍历

二叉树

二叉树的遍历实现

声明文件Bintree.h

#ifndef _BINTREE_H_
#define _BINTREE_H_

#include<stdio.h>
#include<string.h>
#include<Windows.h>
#define ENDTAG '#'

typedef char BTDataType;

typedef struct BinaryTreeNode
{
	BTDataType _data;//当前节点的值域
	struct BinaryTreeNode* _left;//指向当前节点的左孩子
	struct BinaryTreeNode* _right;//指向当前节点的右孩子
}BTNode;

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a);
void BinaryTreeDestory(BTNode* root);

// 递归遍历//深度优先遍历
void BinaryTreePrevOrder(BTNode* root);
void BinaryTreeInOrder(BTNode* root);
void BinaryTreePostOrder(BTNode* root);

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);//广度优先遍历

// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root);

//非递归先中后序遍历
void _BinaryTreePrevOrderNonR(BTNode* root);
void _BinaryTreeInOrderNonR(BTNode* root);
void _BinaryTreePostOrderNonR(BTNode* root);



#endif

实现文件Bintree.c

#include"bintree.h"
#include"queue.h"
#include"stack.h"



// 构造一棵树
BTNode* BinaryTreeCreate(BTDataType* a)//构造一棵树
{
	static int i = 0;
	if (a[i] == ENDTAG)
	{
		i++;
		return NULL;
	}
	else
	{
		BTNode* cur = (BTNode*)malloc(sizeof(BTNode));
		cur->_data = a[i];
		i++;
		cur->_left = BinaryTreeCreate(a);
		cur->_right = BinaryTreeCreate(a);
		return cur;
	}
}
//递归型:前中后序遍历************************************************************************************************************************


void BinaryTreePrevOrder(BTNode* root)//二叉树的先序遍历
{ 
	if (root != NULL)
	{
		printf("%c", root->_data);
		BinaryTreePrevOrder(root->_left);
		BinaryTreePrevOrder(root->_right);
	}
}
void BinaryTreeInOrder(BTNode* root)//二叉树的中序遍历
{
	if (root != NULL)
	{
		BinaryTreeInOrder(root->_left);
		printf("%c", root->_data);
		BinaryTreeInOrder(root->_right);
	}
}
void BinaryTreePostOrder(BTNode* root)//二叉树的后序遍历
{
	if (root != NULL)
	{
		BinaryTreePostOrder(root->_left);
		BinaryTreePostOrder(root->_right);
		printf("%c", root->_data);
	}
}

//层次遍历************************************************************************************************************************

void BinaryTreeLevelOrder(BTNode* root)//层序遍历(树的广度优先搜索)
{
	Queue qu;
	BTNode *tmp;//定义的tmp始终指向队头的data,以此来判断是否有左右孩子,是否需要进行插入操作
	QueueInit(&qu);
	QueuePush(&qu, root);//在队列初始化后,先插入根节点,才能继续下列操作
	while (!QueueEmpty(&qu))//是空返回1,(!1)是表示为假,退出循环
	{
		tmp = QueueFront(&qu); //QueueFront的返回值data是BTNode *类型的,里面包含了一个树节点的data,_left, _right
		printf("%c", tmp->_data);//打印节点的值
		if (tmp->_left)//得到tmp为队列的第一个节点,判断其左孩子是否为空,不为空就让其左孩子进队列
		{
			QueuePush(&qu, tmp->_left);
		}
		if (tmp->_right)//得到tmp为队列的第一个节点,判断其右孩子是否为空,不为空就让其右孩子进队列
		{
			QueuePush(&qu, tmp->_right);
		}
		QueuePop(&qu);//删除队头(已经打印过),更换队头
	}
	QueueDestory(&qu);//摧毁队列
}

//非递归型:前中后序遍历************************************************************************************************************************

//非递归先序遍历
void BinaryTreePrevOrderNonR(BTNode* root)//根左右
{
	Stack st;
	BTNode* cur = root;
	StackInit(&st);
	while (cur||StackEmpty(&st))
	{
		printf("%c", cur->_data);//打印根

		if (cur->_right)//如果有右孩子,就让右孩子进栈
		{
			StackPush(&st, cur->_right);
		}
		if (cur->_left)//如果有左孩子,就让左孩子继续遍历
		{
			cur = cur->_left;
		}
		else//如果左孩子为空时,就让cur取栈顶,并释放原来的栈顶
		{
			cur = StackTop(&st);
			StackPop(&st);
		}
	}
	StackDestory(&st);
}

//非递归中序遍历
void BinaryTreeInOrderNonR(BTNode* root)
{
	BTNode * cur = root;

	Stack st;
	StackInit(&st);

	while (cur || StackEmpty(&st))//当cur为空且栈为空时,循环跳出,代表树遍历完毕
	{
		for (; cur; cur = cur->_left)
			//1,把目前的根及其所有的左孩子压栈,直到左孩子为空为止
			//2,以目前的右孩子为根,继续将它的左孩子压栈
		{
			StackPush(&st, cur);
		}

		//if (StackEmpty(&st))
		//{
			cur = StackTop(&st);
			//1,左孩子遍历完毕后,第一个没有左孩子的结点就是中序的第一个输出
			//2,如果右孩子为空,此时栈里将会是下一个要访问的结点,如果有右孩子,那么此时栈里将会是以那个右孩子
			putchar(cur->_data);
			//1,由于没有左孩子了,所以打印根(左根右)
			StackPop(&st);//出栈
			cur = cur->_right;
			//1,左孩子和根遍历结束后,遍历它的右子树
		//}
	}
	StackDestory(&st);
}

//非递归后序遍历
void BinaryTreePostOrderNonR(BTNode* root)//左右根
{
	BTNode * cur = root;

	Stack st;
	int tag[32] = { 0 };

	StackInit(&st);

	while (cur || StackEmpty(&st))//当cur为空且栈为空时,循环跳出,代表树遍历完毕
	{
		for (; cur; cur = cur->_left)//类似中序,将左孩子入栈,cur为空时,代表上一个节点的右孩子为空,只有这种情况才能进行打印
		{
			StackPush(&st, cur);//push操作会导致size+1
			tag[st._top] = 0;//由于入栈的是左孩子,所以这里的左孩子遍历标签置为0,(是否遍历完毕)
		}
		//只要上面的for只要执行一次,就不能进入while中
		while (StackEmpty(&st) && tag[st._top] == 1)//左孩子没有遍历完毕,不能进行打印
			//所以这里确保了只有左右子树都遍历完成后,才能进行打印
		{
			cur = StackTop(&st);
			putchar(cur->_data);//打印根
			StackPop(&st);//pop操作会导致size-1
			cur = NULL;//为了循环正常跳出
		}

		if (StackEmpty(&st))
		{
			tag[st._top] = 1;//进入这里证明左子树遍历完毕,左子树标签置1
			cur = StackTop(&st)->_right;//进入右子树继续遍历
		}
	}
	StackDestory(&st);
}

//判断一棵树是不是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
	Queue qu;
	BTNode *tmp;
	int leafflag = 0;
	QueueInit(&qu);
	QueuePush(&qu, root);
	while (!QueueEmpty(&qu))
	{
		tmp = QueueFront(&qu); 
		printf("%c", tmp->_data);
		QueuePop(&qu);
		if (leafflag )//当出现有左孩子没有右孩子时,队列里剩余的值都只能有叶子节点,一旦出现左右孩子直接退出
		{
			if (tmp->_left || tmp->_right)
			{
				return 0;
			}
			continue;
		}
		if (tmp->_left&&tmp->_right)
		{
			QueuePush(&qu, tmp->_left);
			QueuePush(&qu, tmp->_right);
		}
		else if (tmp->_right&&!tmp->_left)//有右孩子没有左孩子时,一定不是完全二叉树,直接退出
		{
			return 0;
		}
		else
		{
			leafflag = 1;
			if (tmp->_left)
			{
				QueuePush(&qu, tmp->_left);
			}
		}
	}
	QueueDestory(&qu);
}

void BinaryTreeDestory(BTNode* root)//通过前序遍历来摧毁树
{
	BTNode *left;
	BTNode *right;
	if (root)
	{
		left = root->_left;
		right = root->_right;
		free(root);
		BinaryTreeDestory(left);
		BinaryTreeDestory(right);
	}
}

测试文件test.c

#include"bintree.h"
#include"queue.h"
#include"stack.h"

int main()
{
	//BTNode *BT = BinaryTreeCreate("ABD##E#H##CF##G##");
	BTNode *BT = BinaryTreeCreate("ABD##EJ###CF##G##");
	BinaryTreePrevOrder(BT);
	putchar('\n');
	BinaryTreeInOrder(BT);
	putchar('\n');
	BinaryTreePostOrder(BT);
	putchar('\n');
	BinaryTreeLevelOrder(BT);
	putchar('\n');
	BinaryTreePrevOrderNonR(BT);
	putchar('\n');
	BinaryTreeInOrderNonR(BT);
	putchar('\n');
	BinaryTreePostOrderNonR(BT);
	putchar('\n');
	//printf("%d", BinaryTreeComplete(BT));
	//putchar('\n');

	BinaryTreeDestory(BT);

	system("pause");
	return 0;
}  

在这里插入图片描述

由于二叉树的遍历需要用到一些接口,因此我们引入了以下接口

二叉树的层次遍历需要通过队列来实现,所以我们将队列的接口放到项目中

//queue.h(具体可将上一博客)
#ifndef _QUEUE_H_
#define _QUEUE_H_
#include"bintree.h"
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<Windows.h>

typedef BTNode * QuDataType;//需要传入整个节点,对他进行push和pop操作

typedef struct QueueNode
{
	struct QueueNode* next;
	QuDataType data;
}QueueNode;

typedef struct Queue
{
	QueueNode* _front;
	QueueNode* _rear;
}Queue;

void QueueInit(Queue * pq);//对队列进行初始化
void QueueDestory(Queue* pq);//对队列进行摧毁
QueueNode* BuyQueueNode(QuDataType x);//申请一个队列节点
void QueuePush(Queue* pq, QuDataType x);//队列的尾插操作(先进先出的结构)
void QueuePop(Queue* pq);//队列的头删操作(先进先出的结构)
QuDataType QueueFront(Queue* pq);//得到头结点
int QueueEmpty(Queue* pq);//判断队列是否为空

#endif

//queue.c
#include"queue.h"

//对队列进行初始化
void QueueInit(Queue * pq)
{
	pq->_front = NULL;
	pq->_rear = NULL;
}

//对队列进行摧毁
void QueueDestory(Queue* pq)
{
	QueueNode* tmp;
	for (; pq->_front != NULL; pq->_front = pq->_front->next)
	{
		tmp = pq->_front->next;
		free(pq->_front);
		pq->_front = tmp;
	}
	pq->_front = NULL;
	pq->_rear = NULL;
}

//申请一个队列节点
QueueNode* BuyQueueNode(QuDataType x)
{
	QueueNode* tmp = (QueueNode*)malloc(sizeof(QueueNode));
	tmp->data = x;
	tmp->next = NULL;
	return tmp;
}

//队列的尾插操作(先进先出的结构)
void QueuePush(Queue* pq, QuDataType x)
{
	QueueNode* tmp = BuyQueueNode(x);
	if (pq->_rear == NULL)//如果pq->rear为空的话,就说明这个队列还没有一个节点
	{
		pq->_front = tmp;
		pq->_rear = tmp;
	}
	else
	{
		pq->_rear->next = tmp;
		pq->_rear = tmp;
	}
}

//队列的头删操作(先进先出的结构)
void QueuePop(Queue* pq)
{
	QueueNode* tmp;
	if (pq->_front == NULL)
	{
		return;
	}
	else
	{
		tmp = pq->_front->next;
		free(pq->_front);
		pq->_front = tmp;
	}
}

//得到头结点,返回的data是BTNode *类型的,里面包含了一个树节点的data,_left,_right
QuDataType QueueFront(Queue* pq)
{
	if (pq->_front == NULL)
	{
		return 0;
	}
	else
	{
		return pq->_front->data;
	}
}

//判断队列是否为空
int QueueEmpty(Queue* pq)
{

	return pq->_front == NULL;//是空返回1,不是空返回0
}
//返回队列节点个数

由于非递归的前中后序遍历需要通过栈来实现,因此我们需要引入栈的接口

//stack.h*************************************************************************************
#ifndef _STACK_H_
#define _STACK_H_
#include"bintree.h"
#include<stdio.h>
#include<Windows.h>
#include<assert.h>
#define N 20
typedef BTNode * STDataType;

typedef struct Stack
{
	STDataType *_a;//指向动态开辟的数组
	int _top;//栈顶
	int _capacity;//容量
}Stack;



void StackInit(Stack *ps);//栈的初始化
void StackDestory(Stack *ps);//摧毁栈
void StackPush(Stack *ps, STDataType x);//栈的尾插操作(是一种先进后出的结构)
void StackPop(Stack *ps);//栈的尾删操作(是一种先进后出的结构)
STDataType StackTop(Stack *ps);//返回栈顶的值

int StackEmpty(Stack *ps);
int StackSize(Stack *ps);

#endif

//stack.c**********************************************************
#include"stack.h"
//栈的初始化
void StackInit(Stack *ps)
{
	assert(ps);
	ps->_capacity = N;
	ps->_a = (STDataType*)malloc(ps->_capacity*sizeof(STDataType));
	ps->_top = 0;
}

//摧毁栈
void StackDestory(Stack *ps)
{
	assert(ps);
	if (ps->_a)//防止重复释放
	{
		free(ps->_a);
		ps->_a = NULL;
		ps->_top = 0;
		ps->_capacity = 0;
	}
}


//栈的尾插操作(是一种先进后出的结构)
void StackPush(Stack *ps, STDataType x)
{
	assert(ps);
	if (ps->_top == ps->_capacity)
	{
		ps->_capacity *= 2;
		ps->_a = (STDataType*)malloc(ps->_capacity*sizeof(STDataType));
		assert(ps->_a);
	}
	ps->_a[ps->_top] = x;
	ps->_top++;
}

//栈的尾删操作(是一种先进后出的结构)
void StackPop(Stack *ps)
{
	assert(ps);
	if (ps->_top > 0)
	{
		ps->_top--;
	}
	else
	{
		return NULL;
	}
}

//返回栈顶的值
STDataType StackTop(Stack *ps)
{
	assert(ps);
	if (ps->_top != 0)
	{
		return ps->_a[ps->_top - 1];
	}
	else
	{
		return NULL;
	}
}

//检查是否栈空
int StackEmpty(Stack *ps)
{
	assert(ps);
	if (ps->_top == NULL)
	{
		return 0;//如果为空就返回0
	}
	else
	{
		return 1;//如果不为空就返回1
	}
}
//检查当前栈的大小
int StackSize(Stack *ps)
{
	return ps->_top;
}

猜你喜欢

转载自blog.csdn.net/weixin_44930562/article/details/97527992