【数据结构】实现二叉树

大家好,我是苏貝,本篇博客带大家了解二叉树的实现,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️
在这里插入图片描述


一. 前言

二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。

  1. 顺序存储
    顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储,关于堆我们后面的章节会专门讲解。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

在这里插入图片描述

  1. 链式存储
    二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链三叉链,我们下面用二叉链来实现
    在这里插入图片描述

二. 实现二叉树

1

二叉树的结构体

val是该节点的值,left表示指向该节点的左孩子节点的指针,right表示指向该节点的右孩子节点的指针,

typedef char BTDataType;

typedef struct BinaryTreeNode
{
	BTDataType val;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

2

前中后序遍历和层序遍历

学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础
在这里插入图片描述

按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历

  1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。根左右
  2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。左根右
  3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。左右根

前序/中序/后序的遍历很简单,可以在b站上搜到它们是如何遍历的,这里不多说

层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
在这里插入图片描述

练习:
请写出下面的前序/中序/后序/层序遍历(NULL用N表示)

在这里插入图片描述
前序遍历:ABDNNENNCFNNGNN
中序遍历:NDNBNENANFNCNGN
后序遍历:NNDNNEBNNFNNGCA
层序遍历:ABCDEFG

3

前序遍历

先打印根节点的值,再遍历左子树和右子树

void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	printf("%c ", root->val);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}

用上图例子来展示前序遍历的路程,下面的中序和后序也可以这样画

在这里插入图片描述

4

中序遍历

先遍历左子树,再打印根节点的值,再遍历右子树

void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	BinaryTreeInOrder(root->left);
	printf("%c ", root->val);
	BinaryTreeInOrder(root->right);
}

5

后序遍历

先遍历左子树,再遍历右子树,再打印根节点的值

void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%c ", root->val);
}

6

通过前序遍历的数组构建二叉树

因为我们想通过数组来构建二叉树,所以形参要有数组,我们还需要知道数组的下标,所以我们在调用下面的函数前先定义一个变量来记录数组的下标,为了能让数组下标能被修改,所以实参传的是该变量的地址。也可以不要这个变量而定义一个全局变量,只是要注意,用这种方法时在调用下面函数前,要把全局变量置为0,否则第二次调用函数时数组的下标是从二叉树的个数n开始的。
数组中如果出现“#”,就意味着该处是NULL

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
	if (a[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}

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

	node->val = a[(*pi)++];
	node->left = BinaryTreeCreate(a, pi);
	node->right = BinaryTreeCreate(a, pi);

	return node;
}

7

二叉树的销毁

void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)
		return;
	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);
}

8

二叉树的总节点数

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

9

二叉树的叶子节点个数

int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	if (root->left == NULL && root->right == NULL)
		return 1;
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

10

二叉树的高度/深度

二叉树的高度即树中节点的最大层次

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

11

二叉树的第K层的节点个数

第k层即第一层的第k层,第二层的第k-1层,第三层的第k-2层,……第k层的第1层,所以我们每次遍历都将k-1作为实参,当k==1时表面该节点是第k层的节点

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

12

二叉树查找值为x的节点

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;
	if (root->val == x)
		return root;

	BTNode* ret1 = BinaryTreeFind(root->left, x);
	if (ret1)
		return ret1;

	BTNode* ret2 = BinaryTreeFind(root->right, x);
	if (ret2)
		return ret2;

	return NULL;
}

13

层序遍历

层序遍历是相较于前序/中序/后序最好理解的,它就是将二叉树一层一层的遍历。如下图中,层序遍历的结果是:ABCDEFGH

在这里插入图片描述

虽然它是最好理解的,但它的思想又是较为复杂的。不像前面的3种遍历,层序遍历需要用到队列。

思路:
先将根节点放入队列中,再将根节点删除,删除的同时将根节点的左右孩子节点(除NULL)放入队列中。再将左孩子节点删除,删除的同时再将该节点的左右孩子节点(除NULL)放入队列中……当队列为空时表示层序遍历已完成。那队列中放入的是节点还是指向节点的指针呢?是节点的指针
在这里插入图片描述

那么在写代码之前,我们要先实现队列,详情请点击下面链接
实现队列

void BinaryTreeLevelOrder(BTNode* root)
{
	Quene p;
	QueneInit(&p);
	if (root)
		QuenePush(&p, root);

	while (!QueneEmpty(&p))
	{
		BTNode* front = QueneFront(&p);
		QuenePop(&p);
		printf("%c ", front->val);

		if (front->left)
			QuenePush(&p, front->left);
		if (front->right)
			QuenePush(&p, front->right);
	}
	QueneDestroy(&p);
}

在这里插入图片描述

层序遍历2

上面的代码的结果是将二叉树的所有值打印在一行。那如果我想像二叉树一样,每层只打印二叉树本层的值(如下图),这又该怎么办呢?
在这里插入图片描述

首先,上面代码的大方向是对的,我们只需要改动一小部分。我们需要定义一个变量来计算队列的大小

在这里插入图片描述

void _BinaryTreeLevelOrder(BTNode* root)
{
	Quene p;
	QueneInit(&p);
	if (root)
		QuenePush(&p, root);

	int levelsize = QueneSize(&p);
	while (!QueneEmpty(&p))
	{
		while (levelsize--)
		{
			BTNode* front = QueneFront(&p);
			QuenePop(&p);
			printf("%c ", front->val);

			if (front->left)
				QuenePush(&p, front->left);
			if (front->right)
				QuenePush(&p, front->right);
		}
		printf("\n");
		levelsize = QueneSize(&p);
	}

	QueneDestroy(&p);
}

14

判断二叉树是否为完全二叉树

我们依旧需要利用队列来实现,不过这次即使左右子树为NULL,我们也要将它们插入队列。如果我们最后删除的是NULL,那么就要跳出循环,再判断队列中的元素是否全为NULL,若是,则为完全二叉树,反之则不是完全二叉树

在这里插入图片描述

int BinaryTreeComplete(BTNode* root)
{
	Quene p;
	QueneInit(&p);
	if (root)
		QuenePush(&p, root);

	while (!QueneEmpty(&p))
	{
		BTNode* front = QueneFront(&p);
		QuenePop(&p);

		if (front == NULL)
			break;

		QuenePush(&p, front->left);
		QuenePush(&p, front->right);

	}

	while (!QueneEmpty(&p))
	{
		BTNode* front = QueneFront(&p);
		QuenePop(&p);
		if (front)
		{
			QueneDestroy(&p);
			return 0;
		}
	}

	QueneDestroy(&p);
	return 1;
}


三. 模块化代码实现

BinaryTree.h

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once

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

typedef char BTDataType;

typedef struct BinaryTreeNode
{
	BTDataType val;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int* pi);
// 二叉树销毁
void BinaryTreeDestory(BTNode* root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
//二叉树的深度/高度
int BinaryTreeHeight(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root);

BinaryTree.c

#define _CRT_SECURE_NO_WARNINGS 1


#include"BinaryTree.h"

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
	if (a[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}

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

	node->val = a[(*pi)++];
	node->left = BinaryTreeCreate(a, pi);
	node->right = BinaryTreeCreate(a, pi);

	return node;
}


// 二叉树销毁
void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)
		return;
	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);
}


// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	return root == NULL ? 0 : 
		1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}


// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	if (root->left == NULL && root->right == NULL)
		return 1;
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}


//二叉树的深度/高度
int BinaryTreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;
	int h1 = BinaryTreeHeight(root->left);
	int h2 = BinaryTreeHeight(root->right);
	return h1 > h2 ? h1 + 1 : h2 + 1;
}




// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	return BinaryTreeLevelKSize(root->left, k - 1)
		+ BinaryTreeLevelKSize(root->right, k - 1);
}


// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;
	if (root->val == x)
		return root;

	BTNode* ret1 = BinaryTreeFind(root->left, x);
	if (ret1)
		return ret1;

	BTNode* ret2 = BinaryTreeFind(root->right, x);
	if (ret2)
		return ret2;

	return NULL;
}


// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	printf("%c ", root->val);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}


// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	BinaryTreeInOrder(root->left);
	printf("%c ", root->val);
	BinaryTreeInOrder(root->right);
}


// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%c ", root->val);
}


// 1.层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Quene p;
	QueneInit(&p);
	if (root)
		QuenePush(&p, root);

	while (!QueneEmpty(&p))
	{
		BTNode* front = QueneFront(&p);
		QuenePop(&p);
		printf("%c ", front->val);

		if (front->left)
			QuenePush(&p, front->left);
		if (front->right)
			QuenePush(&p, front->right);

	}

	QueneDestroy(&p);
}


// 2.层序遍历(每一层占一行)
void _BinaryTreeLevelOrder(BTNode* root)
{
	Quene p;
	QueneInit(&p);
	if (root)
		QuenePush(&p, root);

	int levelsize = QueneSize(&p);
	while (!QueneEmpty(&p))
	{
		while (levelsize--)
		{
			BTNode* front = QueneFront(&p);
			QuenePop(&p);
			printf("%c ", front->val);

			if (front->left)
				QuenePush(&p, front->left);
			if (front->right)
				QuenePush(&p, front->right);
		}
		printf("\n");
		levelsize = QueneSize(&p);
	}

	QueneDestroy(&p);
}



// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
	Quene p;
	QueneInit(&p);
	if (root)
		QuenePush(&p, root);

	while (!QueneEmpty(&p))
	{
		BTNode* front = QueneFront(&p);
		QuenePop(&p);

		if (front == NULL)
			break;

		QuenePush(&p, front->left);
		QuenePush(&p, front->right);

	}

	while (!QueneEmpty(&p))
	{
		BTNode* front = QueneFront(&p);
		QuenePop(&p);
		if (front)
		{
			QueneDestroy(&p);
			return 0;
		}
	}

	QueneDestroy(&p);
	return 1;
}

Quene.h

#pragma once

#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
#include"BinaryTree.h"

typedef struct BinaryTreeNode* QDataType;

typedef struct QueneNode
{
	QDataType val;
	struct QueneNode* next;
}QNode;

typedef struct Quene
{
	QNode* phead;
	QNode* ptail;
	int size;
}Quene;


//初始化
void QueneInit(Quene* pq);
//销毁
void QueneDestroy(Quene* pq);
//队尾插入
void QuenePush(Quene* pq, QDataType x);
//队头删除
void QuenePop(Quene* pq);
//显示第一个节点的值
QDataType QueneFront(Quene* pq);
//显示最后一个节点的值
QDataType QueneBack(Quene* pq);
//是否为空
bool QueneEmpty(Quene* pq);
//队列大小
int QueneSize(Quene* pq);

Quene.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"Quene.h"



//初始化
void QueneInit(Quene* pq)
{
	assert(pq);

	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}


//销毁
void QueneDestroy(Quene* pq)
{
	assert(pq);

	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}


//队尾插入
void QuenePush(Quene* pq, QDataType x)
{
	assert(pq);

	//创建一个新节点
	QNode* newnode = (Quene*)malloc(sizeof(Quene));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->next = NULL;
	newnode->val = x;

	//插入
	if (pq->ptail == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = pq->ptail->next;
	}
	pq->size++;
}


//队头删除
void QuenePop(Quene* pq)
{
	assert(pq);
	assert(pq->phead);

	QNode* next = pq->phead->next;
	free(pq->phead);
	pq->phead = next;

	if (pq->phead == NULL)
	{
		pq->ptail = NULL;
	}
	pq->size--;
}


//显示第一个节点的值
QDataType QueneFront(Quene* pq)
{
	assert(pq);

	return pq->phead->val;
}


//显示最后一个节点的值
QDataType QueneBack(Quene* pq)
{
	assert(pq);

	return pq->ptail->val;
}


//是否为空
bool QueneEmpty(Quene* pq)
{
	assert(pq);

	return pq->phead == NULL;
}


//队列大小
int QueneSize(Quene* pq)
{
	assert(pq);

	return pq->size;
}

Test.c

#define _CRT_SECURE_NO_WARNINGS 1


#include"BinaryTree.h"


int main()
{
	// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
	char arr[] = 
	{ 'A','B','D','#','#','E','#','H','#','#','C','F','#','#','G','#','#' };
	int i = 0;
	BTNode* n1 = BinaryTreeCreate(arr, &i);

	printf("BinaryTreeSize = %d\n", BinaryTreeSize(n1));
	printf("BinaryTreeLeafSize = %d\n", BinaryTreeLeafSize(n1));
	printf("BinaryTreeLevelKSize3 = %d\n", BinaryTreeLevelKSize(n1, 3));
	printf("BinaryTreeLevelKSize4 = %d\n", BinaryTreeLevelKSize(n1, 4));

	printf("前序遍历:");
	BinaryTreePrevOrder(n1);
	printf("\n");
	printf("中序遍历:");
	BinaryTreeInOrder(n1);
	printf("\n");
	printf("后序遍历:");
	BinaryTreePostOrder(n1);
	printf("\n");

	BTNode* tmp = BinaryTreeFind(n1, 'E');
	tmp->val = 'K';
	printf("前序遍历:");
	BinaryTreePrevOrder(n1);
	printf("\n");

	printf("层序遍历1:");
	BinaryTreeLevelOrder(n1);
	printf("\n");
	printf("层序遍历2:\n");
	_BinaryTreeLevelOrder(n1);
	printf("\n");

	int ret = BinaryTreeComplete(n1);
	printf("BinaryTreeComplete = %d\n", ret);

	printf("BinaryTreeHeight = %d", BinaryTreeHeight(n1));

	BinaryTreeDestory(n1);
	return 0;
}

结果演示

在这里插入图片描述


好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值