二叉树与堆

目录

一、二叉树

1、二叉树的概念及结构

1.1、树的概念

1.2、二叉树的概念

1.3、二叉树的存储结构

1.3.1、顺序结构存储

1.3.2、链式结构存储

2、二叉树的实现(链式结构)

2.1、二叉树的遍历

2.1.1、前序、中序以及后序遍历

2.1.2、层序遍历

2.2、二叉树链式结构的代码实现

二、堆

1、堆的概念及结构

1.1、堆的概念

1.2、堆的结构

2、堆的实现

2.1、堆的向下调整算法

2.2、堆的创建

2.3、堆的插入

2.4、堆的删除

2.5、堆的代码实现


一、二叉树

1、二叉树的概念及结构

1.1、树的概念

        树是一种非线性的数据结构,它是由n个有限节点组成一个具有层次关系的集合。之所以叫做树是因为它的结构看上去像一棵倒挂的树。如下图所示:

        树有以下特性:

        (1)、树有一个特殊的节点,称为根节点,根节点没有前驱节点。

        (2)、除根节点外,其余节点被分成多个互不相交的集合,每一个集合又是一棵结构与树类似的子树。每棵子树的根节点有且只有一个前驱节点,可以有多个后继节点。

        (3)、树形结构中,子树之间不能有交集,否则就不是树形结构。

        (4)、树是递归定义的。

1.2、二叉树的概念

        二叉树是一种特殊的树形结构,二叉树的每一个节点最多有两个后继节点,有左右之分,次序不可颠倒,是一种有序树。一棵二叉树有根节点、根节点的左子树和根节点的右子树三部分组成。

        两种特殊的二叉树:

        (1)、满二叉树:二叉树的每一层的节点都达到最大值,这个二叉树就是满二叉树。即如果二叉树的层数为k,总结点数是2^k-1,它就是满二叉树。

        (2)、完全二叉树:二叉树除最后一层外的其它层均符合满二叉树的性质,且最后一层从左到右连续,该二叉树就是完全二叉树。满二叉树是一种特殊的完全二叉树。

1.3、二叉树的存储结构

        二叉树一般可以使用两种结构进行数据存储:一种是顺序结构,另一种是链式结构。

1.3.1、顺序结构存储

        顺序结构存储就是使用数组进行存储。这种存储方式只适用于完全二叉树,因为如果使用顺序存储方式来存储不完全二叉树的数据会导致空间的浪费。实际应用中只有堆才会使用顺序存储,有关堆的内容会在下一部分讲到,这里不做介绍。

typedef struct Heap
{
	HPDataType* a;    //指向动态开辟的数组的指针
	int size;        //存储的数据个数
	int capacity;    //数组的容量
}Heap;

1.3.2、链式结构存储

        二叉树的链式存储是指用链表来表示各元素之间的逻辑关系。通常的方法是链表中每个节点有三部分组成,分别是一个数据域和两个左右指针域,左右指针分别用来存储该节点的左孩子节点和右孩子节点的地址。

typedef struct BinaryTreeNode
{
	BTDataType data;    //该节点存储的数据
	struct BinaryTreeNode* left;    //指向左孩子节点
	struct BinaryTreeNode* right;    //指向右孩子节点
}BTNode;

2、二叉树的实现(链式结构)

2.1、二叉树的遍历

        二叉树的遍历是指按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。遍历是对二叉树进行其他运算的基础。

2.1.1、前序、中序以及后序遍历

        (1)、前序遍历:访问根节点的操作发生在遍历其左右子树之前。

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

        (2)、中序遍历:访问根节点的操作发生在遍历其左右子树之间。

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

        (3)、后序遍历:访问根节点的操作发生在遍历其左右子树之后。

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

以前序遍历为例图解分析递归遍历过程:

2.1.2、层序遍历

        不同于前序、中序和后序遍历,层序遍历是对树进行层数上自上而下(即从根节点所在的第一层开始依次向下逐层访问),同一层自左向右的顺序访问树的节点的过程。

(代码中所调用的函数均在本文下一节内容中给出)

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		printf("%c", front->data);
		if (front->left)
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
	}
	printf("\n");
	QueueDestroy(&q);
}

遍历过程如下图:

2.2、二叉树链式结构的代码实现

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

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

// 链式结构:表示队列成员节点
typedef BTNode* QDataType;
typedef struct QListNode
{
	struct QListNode* next;
	QDataType data;
}QNode;

// 队列的结构 
typedef struct Queue
{
	QNode* front;
	QNode* rear;
	int size;
}Queue;

// 初始化队列 
void QueueInit(Queue* q)
{
	assert(q);
	q->front = q->rear = NULL;
	q->size = 0;
}

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{
	assert(q);
	return q->size == 0 ? 1 : 0;
}

// 获取队列头部元素 
QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->front->data;
}

// 队尾入队列 
void QueuePush(Queue* q, QDataType data)
{
	assert(q);

	QNode* NewNode = (QNode*)malloc(sizeof(QNode));
	if (NewNode == NULL)
	{
		perror("malloc:");
		return;
	}
	NewNode->data = data;
	NewNode->next = NULL;
	if (q->size == 0)
	{
		q->front = q->rear = NewNode;
	}
	else
	{
		q->rear->next = NewNode;
		q->rear = q->rear->next;
	}
	q->size++;
}


// 队头出队列 
void QueuePop(Queue* q)
{
	assert(q);
	//assert(!QueueEmpty(q));
	if (q->front == q->rear)
	{
		if (q->front == NULL)
		{
			return;
		}
		else
		{
			free(q->front);
			q->front = q->rear = NULL;
		}
	}
	else
	{
		QNode* Tmp = q->front;
		q->front = Tmp->next;
		free(Tmp);
		Tmp = NULL;
	}
	q->size--;
}

// 销毁队列 
void QueueDestroy(Queue* q)
{
	assert(q);
	if (q->size != 0)
	{
		free(q->front);
		free(q->rear);
		q->front = q->rear = NULL;
	}
}

//创建节点
BTNode* BuyNode(BTDataType x)
{
	BTNode* Node = (BTNode*)malloc(sizeof(BTNode));
	if (Node == NULL)
	{
		perror("BuyNode::malloc:");
		return NULL;
	}
	Node->data = x;
	Node->left = NULL;
	Node->right = NULL;
	return Node;
}

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

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

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

// 二叉树叶子节点个数
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);
}

// 二叉树第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->data == x)
	{
		return root;
	}
	return BinaryTreeFind(root->left, x) || BinaryTreeFind(root->right, x);
}

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

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

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

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		printf("%c", front->data);
		if (front->left)
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
	}
	printf("\n");
	QueueDestroy(&q);
}

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		//遇到空就跳出
		if (front == NULL)
		{
			break;
		}
		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;
}

二、堆

1、堆的概念及结构

1.1、堆的概念

        堆是一棵完全二叉树,堆的每个节点的值总是不大于(大堆)或不小于(小堆)父节点的值。即大堆的根节点最大,小堆的根节点最小。堆只有大堆和小堆两种情况,如果一个完全二叉树既存在父节点的值大于子节点的值的情况,也存在子节点大于父节点的值的情况,那么该完全二叉树就不是堆。

1.2、堆的结构

        堆是用顺序结构的数组来存储的,即堆的物理结构是一个数组,它的逻辑结构是一个完全二叉树。

typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}Heap;

2、堆的实现

2.1、堆的向下调整算法

        一个数组从逻辑上来看就是一棵完全二叉树,我们通过从根节点开始的向下调整算法可以把它调整成一个堆。向下调整算法的前提是左右子树都必须是一个堆,只需将根节点向下调整到合适的位置即可形成堆。

2.2、堆的创建

        一个完全二叉树可以通过向下调整算法或向上调整算法将其构建成一个堆。但是无论是向下调整算法还是向上调整算法都必须满足除待调整节点外其余节点满足堆的性质,所以我们只能从倒数第一个非叶子结点的子树开始向下调整,一直调整到根节点便可将其调整成为一个堆。

2.3、堆的插入

        堆的插入是将待插入元素插入到堆的最后一个孩子节点之后,如果插入后堆的性质被破坏,则需要将新插入的元素向上调整直至重新满足堆的性质。注意:向上调整的前提是除了该叶子节点,其他该叶子节点以上的所有节点满足堆的性质。

2.4、堆的删除

        堆的删除默认删除的堆顶元素。将堆顶数据和堆的最后一个数据进行交换,然后删除最后一个数据,再将根节点进行向下调整直至满足堆的性质即可。(可参考向下调整算法的图解,此处不再重复分析)

2.5、堆的代码实现

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


typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}Heap;

//堆的初始化
void HeapInitial(Heap* hp)
{
	assert(hp);
	hp->a = NULL;
	hp->size = 0;
	hp->capacity = 0;
}

// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n)
{
	//assert(hp);
	HPDataType* tmp = (HPDataType*)malloc(sizeof(HPDataType) * n);
	if (tmp == NULL)
	{
		perror("HeapCreate::malloc:");
		return;
	}
 	hp->a = tmp;
	hp->capacity = n;
	hp->a[0] = a[0];
	for (int i = 1; i < n; i++)
	{
		int child = i;
		hp->a[child] = a[child];
		while (child>0)
		{
			int parent = (child - 1) / 2;
			//小堆:
			if (hp->a[child] < hp->a[parent])
			{
				HPDataType tmp = hp->a[parent];
				hp->a[parent] = hp->a[child];
				hp->a[child] = tmp;
				child = parent;
			}
			else
			{
				break;
			}
		}
	}
	hp->size = n;
}

// 堆的判空(如果为空返回1,非空返回0)
int HeapEmpty(Heap* hp)
{
	assert(hp);
	if (hp->size == 0)
	{
		return 1;
	}
	return 0;
}

// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{
	assert(hp);
	if (!HeapEmpty(hp))
	{
		return hp->a[0];
	}
	return -1;
}

// 堆的数据个数
int HeapSize(Heap* hp)
{
	assert(hp);
	return hp->size;
}

// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);
	if (hp->size == hp->capacity)
	{
		int newcapacity = hp->capacity == 0 ? 5 : 2 * hp->capacity;
		HPDataType* tmp = (HPDataType*)realloc(hp->a,sizeof(HPDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("HeapPush::realloc:");
			return;
		}
		hp->capacity = newcapacity;
	}
	hp->a[hp->size] = x;
	hp->size++;

	//向上调整算法
	int child = hp->size - 1;
	while (child > 0)
	{
		int parent = (child - 1) / 2;
		if (hp->a[child] < hp->a[parent])
		{
			HPDataType tmp = hp->a[parent];
			hp->a[parent] = hp->a[child];
			hp->a[child] = tmp;
			child = parent;
		}
		else
		{
			break;
		}
	}
}

// 堆的删除(默认删除堆顶元素)
void HeapPop(Heap* hp)
{
	assert(hp);
	if (HeapEmpty(hp))
	{
		return;
	}

	//交换首尾元素
	HPDataType tmp = hp->a[0];
	hp->a[0] = hp->a[hp->size - 1];
	hp->a[hp->size - 1] = tmp;

	//删除最后一个元素
	hp->size--;

	//向下调整算法
	int parent = 0;
	while (parent <= (hp->size - 2) / 2)
	{
		int childleft = 2 * parent + 1;
		int childright = 2 * parent + 2;
		int small = hp->a[childleft] < hp->a[childright] ? childleft : childright;
		if (hp->a[parent] > hp->a[small])
		{
			HPDataType tmp1 = hp->a[parent];
			hp->a[parent] = hp->a[small];
			hp->a[small] = tmp1;
			parent = small;
		}
		else
		{
			break;
		}
	}
	for (int i = 1; i < hp->size; i++)
	{
		int child = i;
		while (child > 0)
		{
			int parent = (child - 1) / 2;
			//小堆:
			if (hp->a[child] < hp->a[parent])
			{
				HPDataType tmp = hp->a[parent];
				hp->a[parent] = hp->a[child];
				hp->a[child] = tmp;
				child = parent;
			}
			else
			{
				break;
			}
		}
	}
}

// 堆的销毁
void HeapDestory(Heap* hp)
{
	assert(hp);
	free(hp->a);
	hp->a = NULL;
	hp->capacity = 0;
}

猜你喜欢

转载自blog.csdn.net/libj2023/article/details/132644219