数据结构——二叉树

好,上一篇我们已经讲过了堆,也已经了解了二叉树的基础知识后,我们今天来实现二叉树的相关代码。

由于初始二叉树,由于现在对二叉树结构掌握还不够深入,为了降低学习成本,此处我们来手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。

 

我们已经了解了树的结构基本是下面框架了:

所以我们可以根据上面框架写出以下的结构代码:

定义 

typedef int BTDataType;
typedef struct BinaryTreeNode
{
    数据
	BTDataType data;
    充当左子树
	struct BinaryTreeNode* left;
	充当右子树
    struct BinaryTreeNode* right;
}BTNode;

 

在编写简单的二叉树之前,我们首先得知道下面的类型定义:

以上面这幅图来举例:

简单来说,这几个用到的都要递归的方法。

注意说明:若根左右子树没有数字,即为空的话,用NULL来显示(更直观地看出其中的结论)。

1.前序遍历:根->左子树->右子树。

1)按照上图的例子,这里得到的顺序就是:

1,2,3,NULL,NULL,NULL,4,5,NULL,NULL,6,NULL,NULL.

为什么呢?现在我们来具体讲解:

把它分别看作整体,即上面划分出来的。

因为,它的顺序是根,左子树,右子树

首先,1就是,接着到2是1的左子树,而2又是单独的根。

是不是有得按照它规定的顺序来,根2的左子树是3,3又是单独的根,再寻找3的左子树,即NULL

然后到找3的右子树==NULL,这是不是就意味着2的左子树找完了,之后,才到2的右子树==NULL

完了,到1的右子树==4,4是单独的根,到找4左子树==5,5是单独,找5左子树==NULL,

到5右子树==NULL,完了就回到4的右子树6,6是单独,找6左子树==NULL,6的右子树==NULL

最后返回最开始==结束。

根据上面颜色我们可以发现:每种颜色文字出现的顺序都是:根->左子树->右子树

同时,同种颜色不相邻--->这就是递归的原因。其实这也可以发现明白递归的一下规律了。

 

2.中序遍历:左子树->根->右子树:

有了上面的具体讲解,这里就不过多的讲解了,下面给出它的顺序,可以自己来验证一下:

NULL,3,NULL,2,NULL,1,NULL,5,NULL,4,NULL,6,NULL

 

3.后序遍历:左子树->右子树->根

同样:

NULL,NULL,3,NULL,2,NULL,NULL,5,NULL,NULL,6,4,1

 

层序遍历:一层一层算过去

1,2,3,4,5,6

 

好了,了解了之后,我们来用代码实现一下。

我们还是得malloc一下

BuyNode部分

BTNode* BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

	node->data = x;
	node->left = NULL;
	node->right = NULL;

	return node;
}

这相当于初始化嘛,先malloc,创建好开始的。相当于:

 

初步之后,我们开始搭建树的结构了。

CreatTree部分

BTNode* CreatTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
	BTNode* node7 = BuyNode(7);


	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	node3->right = node7;

	return node1;
}

搭建好树的结构后,再想要是前序还是中序或是后序都变得非常简单了。

前序遍历:

void PreOrder(BTNode* root) {
	if (root == NULL) {
		printf("NULL ");
		return;
	}

	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

但是呢,如果有疑惑的话,不妨我们画出它的递归展开图:

上面就是它递归的全过程。

这里是不是就是前序的规则得出:

1,2,4,NULL,NULL,5,NULL,NULL,3,6,NULL,NULL,7,NULL,NULL.

上面我们写的对了。

 

中序遍历

void InOrder(BTNode* root) {
	if (root == NULL) {
		printf("NULL ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
    InOrder(root->right);
}

它的递归展开图:

 

后序遍历 

void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}

	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

同样的递归图,就不一一画了,可以试试画,你会更清楚的。

 

求节点的个数

怎么求呢?

就以学校的关系来举例吧:

一个校长要求算出整个学校的人数总数,那么会有:

1)校长亲自去一个一个班去数人头个数。

2)吩咐各级领导统计,逐级上报。

这样上报人数明显更加方便,对吧?

同样方法一,就像是第一种情况,能不能干?可以干,但是效率太低了。 

方法一:

int size = 0;
void TreeSize(BTNode* root)
{
	if (root == NULL)
		return;

	++size;
	TreeSize(root->left);
	TreeSize(root->right);
}

如果不想把size弄外面的话也可以下面:

void TreeSize(BTNode* root, int* psize)
{
	if (root == NULL)
		return;

	++(*psize);
	TreeSize(root->left, psize);
	TreeSize(root->right, psize);
}

所以我们第二种方法:

int TreeSize(BTNode* root)
{
	return root == NULL ? 0 : 
			TreeSize(root->left) 
			+ TreeSize(root->right) 
			+ 1;
}

加1是要把它本身也要加上。

它的递归展开图可以看出它的全过程。 

 

求树的高度

比如说上图: 得出:

当前树的高度=左右子树高的那个+1

int TreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;

	return TreeHeight(root->left) > TreeHeight(root->right)
		? TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;
}

有人可能会写出这样的一种代码,它虽然行,但是效率低。

为什么呢?这相当于你每次当每级人员把自己级的数据报给上级领导后,他没有记录下来,等到他要真正记录上报给他上级时,忘了数据,又要重新问,而你之前上报时也没有记录下来,也忘记了,这样每个人都忘记了,所以会造成每次一个级的领导要的时候,又要重新来一遍。这样的数据累计起来就非常多了。

所以,真正的是要把每次的数据记录下来,避免效率低下。

int TreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;

	int leftHeight = TreeHeight(root->left);
	int rightHeight = TreeHeight(root->right);

	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

 

求在k层的个数

 

当前树第k层个数=左子树的第k-1层个数+右子树的第k-1层个数

 

int TreeKLevel(BTNode* root, int k)
{
	assert(k > 0);
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	return TreeKLevel(root->left, k - 1)
		+ TreeKLevel(root->right, k - 1);
}

递归展开图: 

 

 Find部分

BTNode* TreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;
	if (root->data == x)
		return root;
	BTNode* lret = TreeFind(root->left, x);
	if (lret)   //返回的不为空就进
		return lret;
	BTNode* rret = TreeFind(root->right, x);
	if (rret)
		return rret;
	return NULL;
}

它的递归展开图:

找下图中的6数字

 

层序遍历

这里需要用到队列的代码,因为,之前在堆那里讲到我们树实质是数组

而队列的规则:先进先出,    这符合我们的层序遍历。

这里,我们就只要把之前写过的Queue队列的代码形成文件,拿过来就可以了 。

但是呢:需要注意的是:

一定一定要更改*********不然就会错!!!!!(问就是我没改,找了好久没找到!!!!)

因为你不同类型了,现在是树,之前只是整形

原来的
//typedef int  QDatatype; 
改后的
typedef struct BinaryTreeNode*  QDatatype;

typedef struct QueueNode
{
	struct QueueNode* next;
	QDatatype data;
}QNode;

这里的原理就是:

出一层,带入下一层

void LevelOrder(BTNode* root)
{
	Queue q;
    //初始化
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{

       注意: 这里不是int*
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		printf("%d ", front->data);

		if(front->left)
			QueuePush(&q, front->left);

		if (front->right)
			QueuePush(&q, front->right);
	}

	QueueDestroy(&q);
}

解读:

1.首先,肯定是要把一开始的根push进去队列

2.正式进入循环(不为空)

3.要记录下对头的数据,防止之后Pop删除之后,找不到,而无法打印出这个数字。

4.接着Push左子树,右子树。

5.队列要进行开始初始化,结束要销毁,防止内存泄漏。

 

判断是否是完全二叉树

 

这里的方法也是要使用到队列的知识点

我们之前了解过,完全二叉树的概念:除了最后一层,都是满的二叉树,且最后一层从做开始,是连续的,中间不能为空。

按照这种情况,我们不妨有以下思路:

利用层序遍历,(出一层,带入下一层),直到出现空的前一个数,再进行循环,如果后面空中出现了非空,则证明不是。

如上图,5和6出完之后,剩下的全都是空,说明它是完全二叉树的。 

bool TreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
    //层序遍历
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		if (front == NULL)
		{
			break;
		}
		else
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
	}

	// 判断是不是完全二叉树
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		// 后面有非空,说明非空节点不是完全连续
		if (front)
		{
			QueueDestroy(&q);
			return false;
		}
	}

	QueueDestroy(&q);
	return true;
}

 

最后给出代码:

test部分

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

#include "Queue.h"

typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

BTNode* BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

	node->data = x;
	node->left = NULL;
	node->right = NULL;

	return node;
}

BTNode* CreatTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
	BTNode* node7 = BuyNode(7);


	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	node2->right = node7;

	return node1;
}

void PreOrder(BTNode* root) {
	if (root == NULL) {
		printf("NULL ");
		return;
	}

	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

void InOrder(BTNode* root) {
	if (root == NULL) {
		printf("NULL ");
		return;
	}

	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}

	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}


int TreeSize(BTNode* root)
{
	return root == NULL ? 0 : 
			TreeSize(root->left) 
			+ TreeSize(root->right) 
			+ 1;
}



int TreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;

	int leftHeight = TreeHeight(root->left);
	int rightHeight = TreeHeight(root->right);

	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

int TreeKLevel(BTNode* root, int k)
{
	assert(k > 0);

	if (root == NULL)
		return 0;
	
	if (k == 1)
		return 1;

	return TreeKLevel(root->left, k - 1)
		+ TreeKLevel(root->right, k - 1);
}

// 二叉树查找值为x的结点
BTNode* TreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;

	if (root->data == x)
		return root;

	BTNode* lret = TreeFind(root->left, x);
	if (lret)
		return lret;

	BTNode* rret = TreeFind(root->right, x);
	if (rret)
		return rret;

	return NULL;
}


void LevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		printf("%d ", front->data);

		if(front->left)
			QueuePush(&q, front->left);

		if (front->right)
			QueuePush(&q, front->right);
	}

	printf("\n");
	QueueDestroy(&q);
}

// 判断二叉树是否是完全二叉树
bool TreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		if (front == NULL)
		{
			break;
		}
		else
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
	}

	// 判断是不是完全二叉树
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		// 后面有非空,说明非空节点不是完全连续
		if (front)
		{
			QueueDestroy(&q);
			return false;
		}
	}

	QueueDestroy(&q);
	return true;
}

// 二叉树销毁
void TreeDestory(BTNode* root)
{
	if (root == NULL)
		return;

	TreeDestory(root->left);
	TreeDestory(root->right);
	free(root);
}

int main()
{
	BTNode* root = CreatTree();
	PreOrder(root);
	printf("\n");

	InOrder(root);
	printf("\n");

	PostOrder(root);
	printf("\n");

	/*size = 0;
	TreeSize(root);
	printf("TreeSize:%d\n", size);

	size = 0;
	TreeSize(root);
	printf("TreeSize:%d\n", size);*/

	/*int size1 = 0;
	TreeSize(root, &size1);
	printf("TreeSize:%d\n", size1);

	int size2 = 0;
	TreeSize(root, &size2);
	printf("TreeSize:%d\n", size2);*/

	printf("TreeSize:%d\n", TreeSize(root));
	printf("TreeSize:%d\n", TreeSize(root));
	printf("TreeSize:%d\n", TreeSize(root));
	printf("TreeHeight:%d\n", TreeHeight(root));
	printf("TreeKLevel:%d\n", TreeKLevel(root, 3));
	printf("TreeKLevel:%d\n", TreeKLevel(root, 4));

	printf("TreeFind:%p\n", TreeFind(root, 5));
	printf("TreeFind:%p\n", TreeFind(root, 50));

	LevelOrder(root);

	printf("TreeComplete:%d\n", TreeComplete(root));


	TreeDestory(root);
	root = NULL;

	return 0;
}

Queue.c部分

#include"Queue.h"

void QueueInit(Queue* pq)
{
	assert(pq);

	pq->head = pq->tail = NULL;
	pq->size = 0;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	pq->head = pq->tail = NULL;
	pq->size = 0;
}

void QueuePush(Queue* pq, QDatatype x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;

	if (pq->head == NULL)
	{
		assert(pq->tail == NULL);

		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}

	pq->size++;
}

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head != NULL);

	/*QNode* next = pq->head->next;
	free(pq->head);
	pq->head = next;

	if (pq->head == NULL)
		pq->tail = NULL;*/

	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}

	pq->size--;
}

int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->size == 0;
}

QDatatype QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->head->data;
}

QDatatype QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->tail->data;
}

    Queue.h部分

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

typedef struct BinaryTreeNode*  QDatatype;

typedef struct QueueNode
{
	struct QueueNode* next;
	QDatatype data;
}QNode;

typedef struct Queue
{
	QNode* head;
	QNode* tail;
	int size;
}Queue;


void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq, QDatatype x);
void QueuePop(Queue* pq);
int QueueSize(Queue* pq);
bool QueueEmpty(Queue* pq);
QDatatype QueueFront(Queue* pq);
QDatatype QueueBack(Queue* pq);

每次鸡汤部分: 

最后,新的一年,希望大家都学有所成,勇敢飞。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值