二叉树与堆

这篇具有很好参考价值的文章主要介绍了二叉树与堆。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、二叉树

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个有限节点组成一个具有层次关系的集合。之所以叫做树是因为它的结构看上去像一棵倒挂的树。如下图所示:

二叉树与堆,数据结构,c语言

        树有以下特性:

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

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

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

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

1.2、二叉树的概念

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

        两种特殊的二叉树:

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

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

二叉树与堆,数据结构,c语言

1.3、二叉树的存储结构

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

1.3.1、顺序结构存储

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

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

二叉树与堆,数据结构,c语言

1.3.2、链式结构存储

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

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

二叉树与堆,数据结构,c语言

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);
}

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

二叉树与堆,数据结构,c语言

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);
}

遍历过程如下图:

二叉树与堆,数据结构,c语言

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;

二叉树与堆,数据结构,c语言

2、堆的实现

2.1、堆的向下调整算法

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

二叉树与堆,数据结构,c语言

2.2、堆的创建

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

二叉树与堆,数据结构,c语言

2.3、堆的插入

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

二叉树与堆,数据结构,c语言

2.4、堆的删除

        堆的删除默认删除的堆顶元素。将堆顶数据和堆的最后一个数据进行交换,然后删除最后一个数据,再将根节点进行向下调整直至满足堆的性质即可。(可参考向下调整算法的图解,此处不再重复分析)文章来源地址https://www.toymoban.com/news/detail-694873.html

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;
}

到了这里,关于二叉树与堆的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构】树与二叉树

    树是一种 非线性的数据结构 ,它是由n(n=0)个有限结点组成一个具有层次关系的集合。 把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的 。它具有以下的特点: 有一个特殊的结点,称为根结点,根结点没有前驱结点 除根结点外,其余结点被分

    2024年02月11日
    浏览(42)
  • 数据结构_树与二叉树

    目录 1. 树的基本概念 1.1 树的定义 1.2 基本术语 1.3 树的性质 1.4 相关练习 2. 二叉树的概念 2.1 二叉树的概念及其主要特性 2.2 二叉树的存储结构 2.2.1 顺序存储结构 2.2.2 链式存储结构 2.3 相关练习 3. 二叉树的遍历和线索二叉树 3.1 二叉树的遍历 3.1.1 先序遍历 3.1.2 中序遍历 3.1

    2024年02月04日
    浏览(45)
  • 【数据结构】树与二叉树(中)

    目录 前言: 一、顺序存储结构: 二、堆: 1.堆的性质: 2.堆的性质: 3.堆的实现: Ⅰ.堆的初始化:  Ⅱ.堆的插入(含向上调整):  Ⅲ.堆的删除(含向下调整算法): Ⅳ.取堆顶的数据: Ⅴ.堆中的数据个数: Ⅵ.堆的判空:  Ⅶ.堆的销毁: 总结:         上篇文章中,

    2024年02月16日
    浏览(45)
  • 数据结构:图文详解 树与二叉树(树与二叉树的概念和性质,存储,遍历)

    目录 一.树的概念 二.树中重要的概念 三.二叉树的概念 满二叉树 完全二叉树 四.二叉树的性质 五.二叉树的存储 六.二叉树的遍历 前序遍历 中序遍历  后序遍历  树是一种 非线性数据结构 ,它由节点和边组成。树的每个节点可以有零个或多个子节点,其中一个节点被指定为

    2024年02月04日
    浏览(49)
  • 数据结构与算法——树与二叉树

    😊各位小伙伴久等了,本专栏新文章出炉了!!! 我又回来啦,接下来的时间里,我会持续把数据结构与算法专栏更新完。 👉树型结构👈 是一类重要的 ✍非线性数据结构 ,其中以树和二叉树最为常用,直观来看,树是以分支关系定义的层次结构。树型结构在客观世界中

    2024年02月11日
    浏览(43)
  • 【数据结构与算法】树与二叉树

    除了之前我们讲的栈、队列、链表等线性结构,数据结构中还有着一对多的 非线性结构 ——— 树 。 树是有 n 个结点组成的有限集,当n=0时为空树,在任意一颗非空树中,有且仅有一个 特定的根结点 ;当n1时,其余结点又可以分为一棵树,称为根的 子树 。 如下图所示: A为

    2023年04月09日
    浏览(47)
  • 【数据结构】树与二叉树(十三):递归复制二叉树(算法CopyTree)

      二叉树是一种常见的树状数据结构,它由结点的有限集合组成。一个二叉树要么是 空集 ,被称为 空二叉树 ,要么由一个根结点和两棵不相交的子树组成,分别称为 左子树 和 右子树 。每个结点最多有两个子结点,分别称为左子结点和右子结点。 引理5.1:二叉树中层数

    2024年02月01日
    浏览(37)
  • 数据结构与算法——树与二叉树篇详解

    树形结构是一种非常重要的 非线性结构 ,树形结构中数据元素之间具有 一对多 的逻辑关系。 1.1.1 树的定义 树是由n(n=0)个结点所构成的有限集合 当n=0时,称为空树 当n0时,n个结点满足以下条件 有且仅有一个称为根的结点 其余结点可分为m个互不相交的有限集合,且每一个

    2024年02月06日
    浏览(50)
  • 【数据结构】24王道考研笔记——树与二叉树

    树是n个结点的有限集合,n=0时,称为空树。非空树满足: 除了根节点外,任何一个结点都有且仅有一个前驱 结点的层次(深度):从上往下数 结点的高度:从下往上数 树的高度(深度):总共有多少层 结点的度:有几个孩子(分支) 树的度:各节点的度的最大值 森林:

    2024年02月13日
    浏览(47)
  • 数据结构之树与二叉树——算法与数据结构入门笔记(五)

    本文是算法与数据结构的学习笔记第五篇,将持续更新,欢迎小伙伴们阅读学习。有不懂的或错误的地方,欢迎交流 前面章节介绍的都是线性存储的数据结构,包括数组、链表、栈、队列。本节带大家学习一种非线性存储的数据结构,即树(tree)。 不管是在面试时,还是日

    2024年02月08日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包