『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码)

这篇具有很好参考价值的文章主要介绍了『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码),新星计划免费学习专栏·数据结构与算法,算法,数据结构,c语言,堆,二叉树


『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码),新星计划免费学习专栏·数据结构与算法,算法,数据结构,c语言,堆,二叉树

目录

0.写在前面

1.什么是堆?

2.堆的实现

2.1 堆的结构定义

2.2 函数声明

2.3 函数实现

2.3.1 AdjustUp(向上调整算法)

2.3.2 AdjustDown(向下调整算法)

2.3.3 HeapCreate(如何建堆)

2.3.4 建堆的时间复杂度

3. 完整源码

Heap.h文件

Heap.c文件 

Test.c文件 


0.写在前面

上一章中介绍了树和二叉树的概念及结构,本章我们将学习堆的实现。其中涉及若干树和二叉树的概念,如需查看,请点击链接跳转。

想要学会二叉树?树的概念与结构是必须要掌握的!快进来看看吧http://t.csdn.cn/GWQJy

1.什么是堆?

堆是一种完全二叉树。只不过堆是二叉树顺序结构的实现,说白了就是将一个数组看作二叉树。也就是说,堆的逻辑结构是一棵二叉树,存储结构是数组。

堆又分为大堆和小堆:

大堆:树中所有父亲都大于等于孩子;

小堆:树中所有父亲都小于等于孩子。

注意,不满足这两点的二叉树不能称为堆(这点很重要)。

『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码),新星计划免费学习专栏·数据结构与算法,算法,数据结构,c语言,堆,二叉树

2.堆的实现

2.1 堆的结构定义

typedef int HPDataType;

typedef struct Heap
{
	HPDataType* a;  //存储数据
	int size;		//堆有效数据的大小
	int capacity;	//堆的容量
}Heap;

上文讲到,堆其实就是二叉树的顺序结构实现,所以用一个数组来存储数据。

2.2 函数声明

//给出一个数组,对它进行建堆
void HeapCreate(Heap* php, HPDataType* a, int n);
//堆的初始化
void HeapInit(Heap* php);
//对申请的内存释放
void HeapDestroy(Heap* php);
//添加数据
void HeapPush(Heap* php, HPDataType data);
//删除数据
void HeapPop(Heap* php);
//向上调整算法
void AdjustUp(HPDataType* a, int child);
//向下调整算法
void AdjustDown(HPDataType* a, int n, int parent);
//打印堆的数据
void HeapPrint(Heap* php);
//判断堆是否为空
bool HeapEmpty(Heap* php);
//返回堆的大小
int HeapSize(Heap* php);
//返回堆顶的数据
HPDataType HeapTop(Heap* php);
//交换函数
void Swap(HPDataType* p1, HPDataType* p2);

2.3 函数实现

由于堆的实现所用函数较多,这里就挑其中最难也是最重要的进行说明。

2.3.1 AdjustUp(向上调整算法)

当我们要实现在HeapPush(堆中添加数据data时),我们的做法是先将data插入到堆的尾部,再将data进行向上调整,直到它到达合适的位置。

如图,假设现在要将data=60添加到下面这个大堆中。

『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码),新星计划免费学习专栏·数据结构与算法,算法,数据结构,c语言,堆,二叉树

第一步,将60插入到堆的末尾,即数组的末尾。

『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码),新星计划免费学习专栏·数据结构与算法,算法,数据结构,c语言,堆,二叉树

第二步,比较60与它父亲节点的大小。因为要保证插入数据之后堆仍然是大堆,所以如果60大于父亲,则交换位置。

『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码),新星计划免费学习专栏·数据结构与算法,算法,数据结构,c语言,堆,二叉树

第三步,继续比较60与父亲的值,若大于父亲则交换位置。

『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码),新星计划免费学习专栏·数据结构与算法,算法,数据结构,c语言,堆,二叉树

至此,60已经到它正确的位置上了。

以上就是向上调整的过程,来看看代码实现。

void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;

	while (child > 0)
	{
		//建大堆用'>',小堆用'<'
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void HeapPush(Heap* php, HPDataType data)
{
	assert(php);
	//如果容量不足就扩容
	if (php->size == php->capacity)
	{
		int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType)*newCapacity);

		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		php->a = tmp;
		php->capacity = newCapacity;
	}
	//添加数据
	php->a[php->size] = data;
	php->size++;
	//将新入堆的data进行向上调整
	AdjustUp(php->a, php->size-1);
}

2.3.2 AdjustDown(向下调整算法)

当我们要实现HeapPop(删除堆顶的数据)时,我们不能像往常的数组那样进行头删。因为数组再进行头删时,还要将从第二个位置起的后面的所有元素都向前平移。

但是堆这样行不通,因为随意挪动数据会造成关系的混乱。例如:

『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码),新星计划免费学习专栏·数据结构与算法,算法,数据结构,c语言,堆,二叉树

此时这个二叉树结构就不再是一个大堆了。

那么有什么好的办法不使堆的结构紊乱呢?这就得用到向下调整算法了。

例如,假设此时要删除堆顶的数据:『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码),新星计划免费学习专栏·数据结构与算法,算法,数据结构,c语言,堆,二叉树

第一步,交换堆顶与堆尾的值,并将堆的Size--(相当于删除了末尾的元素)。

『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码),新星计划免费学习专栏·数据结构与算法,算法,数据结构,c语言,堆,二叉树

第二步,对45进行向下调整。找出45的两个孩子中值最大(是小堆就选小的)的那个,如果5小于该数字就与其进行交换。

『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码),新星计划免费学习专栏·数据结构与算法,算法,数据结构,c语言,堆,二叉树

循环此步骤,直至45到达正确的位置。

显然,此时情况较为简单,只用一步就到达了正确位置。(此时70已经不存在了,所以不用比较)

以上就是向下调整的过程,来看看代码的实现。

void AdjustDown(HPDataType* a, int n, int parent)
{
	assert(a);
	//先默认较大的为左孩子
	int child = parent * 2+1;
	while (child<n)
	{
		//如果右孩子比左孩子大,就++
		if (a[child] < a[child + 1] && child + 1 < n)
		{
			child++;
		}
		//建大堆用'>',小堆用'<'
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void HeapPop(Heap* php)
{
	assert(php);
	assert(php->size>0);

	//将堆顶的数据与堆尾交换
	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;
	//将此时堆顶的data向下调整
	AdjustDown(php->a, php->size, 0);
}

特别注意:不管是向上调整还是向下调整,它们都得满足一个前提->

向上调整:进行向上调整的时候,该位置之前的所有数据已经是一个堆了。

向下调整:进行向下调整的时候,该位置的左子树和右子树都已经是堆了。

2.3.3 HeapCreate(如何建堆)

此函数所实现的功能是给出一个数组,对数组进行建堆(建大堆或者小堆)。

先来看看代码实现:

void HeapCreate(Heap* php, HPDataType* a, int n)
{
	php->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
	if (php->a == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	//将数组的内容全部拷贝到php->a中
	memcpy(php->a, a, sizeof(HPDataType) * n);
	php->size = php->capacity = n;

	//建堆算法
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(php->a, n, i);
	}
}

这里采用向下调整算法的的思路是,既然向下调整和向上调整都是有前提的,就不能直接进行使用。但是我们发现即使这个二叉树的数据是紊乱的,但是总有可以当作堆的一部分来使用向下调整(不用向上调整是因为不好控制)。例如:

『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码),新星计划免费学习专栏·数据结构与算法,算法,数据结构,c语言,堆,二叉树

在这个堆的底部(3个黑色圆圈里的部分)可以看作是堆,可以满足进行向下调整的条件。当把底层的三个堆建好以后,我们发现两个红色圆圈中的部分又可以看作满足条件的堆,对这两部分在进行向下调整。

『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码),新星计划免费学习专栏·数据结构与算法,算法,数据结构,c语言,堆,二叉树

完成之后,我们发现堆顶元素的左子树和右子树都已经是堆了,最后再将堆顶的元素进行向下调整,就建好一个完整的堆了。

总结起来就是如下图的步骤:

『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码),新星计划免费学习专栏·数据结构与算法,算法,数据结构,c语言,堆,二叉树

2.3.4 建堆的时间复杂度

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个节点不影响最终结果):

『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码),新星计划免费学习专栏·数据结构与算法,算法,数据结构,c语言,堆,二叉树

因此,建堆的时间复杂度为O(N)。

3. 完整源码

Heap.h文件

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

typedef int HPDataType;

typedef struct Heap
{
	HPDataType* a;   //存储数据
	int size;				//堆有效数据的大小
	int capacity;			//堆的容量
}Heap;

//给出一个数组,对它进行建堆
void HeapCreate(Heap* php, HPDataType* a, int n);
//堆的初始化
void HeapInit(Heap* php);
//对申请的内存释放
void HeapDestroy(Heap* php);
//添加数据
void HeapPush(Heap* php, HPDataType data);
//删除数据
void HeapPop(Heap* php);
//向上调整算法
void AdjustUp(HPDataType* a, int child);
//向下调整算法
void AdjustDown(HPDataType* a, int n, int parent);
//打印堆的数据
void HeapPrint(Heap* php);
//判断堆是否为空
bool HeapEmpty(Heap* php);
//返回堆的大小
int HeapSize(Heap* php);
//返回堆顶的数据
HPDataType HeapTop(Heap* php);
//交换函数
void Swap(HPDataType* p1, HPDataType* p2);

Heap.c文件 

#define _CRT_SECURE_NO_DEPRECATE 1
#include"Heap.h"

void HeapCreate(Heap* php, HPDataType* a, int n)
{
	php->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
	if (php->a == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	//将数组的内容全部拷贝到堆中
	memcpy(php->a, a, sizeof(HPDataType) * n);
	php->size = php->capacity = n;

	//建堆算法
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(php->a, n, i);
	}
}

void HeapInit(Heap* php)
{
	assert(php);

	php->a = NULL;
	php->size = php->capacity = 0;
}

void HeapPrint(Heap* php)
{
	assert(php);

	for (int i = 0; i < php->size; i++)
	{
		printf("%d ", php->a[i]);
	}
}

void HeapDestroy(Heap* php)
{
	assert(php);

	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;
}

void HeapPush(Heap* php, HPDataType data)
{
	assert(php);
	//如果容量不足就扩容
	if (php->size == php->capacity)
	{
		int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType)*newCapacity);

		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		php->a = tmp;
		php->capacity = newCapacity;
	}
	//添加数据
	php->a[php->size] = data;
	php->size++;
	//将新入堆的data进行向上调整
	AdjustUp(php->a, php->size-1);
}

void HeapPop(Heap* php)
{
	assert(php);
	assert(php->size>0);

	//将堆顶的数据与堆尾交换
	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;
	//将此时堆顶的data向下调整
	AdjustDown(php->a, php->size, 0);
}

void AdjustDown(HPDataType* a, int n, int parent)
{
	assert(a);
	//先默认较大的为左孩子
	int child = parent * 2+1;
	while (child<n)
	{
		//如果右孩子比左孩子大,就++
		if (a[child] < a[child + 1] && child + 1 < n)
		{
			child++;
		}
		//建大堆用'>',小堆用'<'
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;

	while (child > 0)
	{
		//建大堆用'>',小堆用'<'
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

HPDataType HeapTop(Heap* php)
{
	assert(php);
	assert(php->size>0);

	return php->a[0];
}

int HeapSize(Heap* php)
{
	assert(php);

	return php->size;
}

bool HeapEmpty(Heap* php)
{
	assert(php);

	return !php->size;
}

void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *(p1);
	*(p1) = *(p2);
	*(p2) = tmp;
}

Test.c文件 

#define _CRT_SECURE_NO_DEPRECATE 1

#include"Heap.h"

void test()
{
	HPDataType arr[10] = { 12,34,45,78,56,74,3,7,9,5 };
	Heap hp;
	HeapCreate(&hp, arr, 10);
	//HeapInit(&hp);
	//HeapPush(&hp, 10);
	//HeapPush(&hp, 70);
	//HeapPush(&hp, 15);
	//HeapPush(&hp, 30);
	//HeapPush(&hp, 25);
	//HeapPrint(&hp);
	//HeapPop(&hp);
	//HeapPrint(&hp);
	//HeapPop(&hp);
	//HeapPrint(&hp);
	//HeapPop(&hp);
	HeapPrint(&hp);
}
int main()
{
	test();
	return 0;
}

至此,本章的内容就结束了,下一章将进行堆的实际应用——堆排序以及TopK问题的说明。

『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码),新星计划免费学习专栏·数据结构与算法,算法,数据结构,c语言,堆,二叉树文章来源地址https://www.toymoban.com/news/detail-594225.html

到了这里,关于『初阶数据结构 • C语言』⑫ - 堆的概念&&实现(图文详解+完整源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 【初阶数据结构】——堆的引入

    目录 前言 一、二叉树的顺序结构及实现  1.1二叉树的顺序结构 1.2堆的结构 二、堆的实现 2.1堆向上调整算法(堆的插入) 2.2堆向下调整算法(堆的删除) 2.3建堆的时间复杂度 2.4堆的创建 2.5堆的初始化和空间的销毁 2.6堆的插入 向上调整函数 交换函数 2.7堆的删除 向下调整

    2024年02月07日
    浏览(38)
  • 【数据结构】--- 博主拍了拍你并向你扔了一“堆”二叉树(堆的概念+结构+代码实现)

    👧个人主页:@小沈熬夜秃头中୧⍤⃝❅ 😚小编介绍:欢迎来到我的乱七八糟小星球🌝 📋专栏:数据结构 🔑本章内容:二叉树—堆 送给各位💌:心有所期全力以赴定有所成. 记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~ 提示:以下是本篇文章正文内容,下面案例可供参考

    2024年02月06日
    浏览(38)
  • 数据结构入门(C语言版)二叉树的顺序结构及堆的概念及结构实现应用

    普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用 顺序结构的数组来存储 ,需要注意的是 这里的堆和操作系统虚拟进程地址空间中的堆是两回事 ,一个是 数据结构 ,一

    2023年04月19日
    浏览(52)
  • 【数据结构】树二叉树的概念以及堆的详解

    ✨链接1:【数据结构】顺序表 ✨链接2:【数据结构】单链表 ✨链接3:【数据结构】双向带头循环链表 ✨链接4:【数据结构】栈和队列 百度百科的解释 :树是一种 非线性 的数据结构,它是由n(n≥0)个有限节点组成一个具有层次关系的集合。 把它叫做树是因为它看起来像

    2024年02月16日
    浏览(39)
  • 【数据结构初阶】之堆(C语言实现)

    前言 :在二叉树基础篇我们提到了二叉树的顺序实现,今天让我们来学习一下特殊的二叉树———堆的相关知识。 📃 博客主页: 小镇敲码人 💞 热门专栏:数据结构与算法 🚀 欢迎关注:👍点赞 👂🏽留言 😍收藏 🌏 任尔江湖满血骨,我自踏雪寻梅香。 万千浮云遮碧月

    2024年04月09日
    浏览(82)
  • 数据结构初阶(用C语言实现简单数据结构)--栈和队列

    ✨✨欢迎来到T_X_Parallel的博客!!       🛰️博客主页:T_X_Parallel       🛰️专栏 : 数据结构初阶       🛰️欢迎关注:👍点赞🙌收藏✍️留言 这小猫真好看 言归正传,通过上篇有关顺序表和链表的博客,可以了解到线性表的一些大致特征,这篇博

    2024年02月08日
    浏览(42)
  • 数据结构初阶之顺序表(C语言实现)

    顺序表是数据结构里面很基础的一类,它是线性表的一种,其它线性表还有链表、栈和队列等,今天来和博主一起学习关于顺序表的知识吧。 顺序表,它分为两类: 动态顺序表 和 静态顺序表 ,这两个表的区别就是 前者的空间不固定 ,是 支持扩容 的,后者的 空间是固定

    2024年02月03日
    浏览(45)
  • 数据结构学习记录——什么是堆(优先队列、堆的概念、最大堆最小堆、优先队列的完全二叉树表示、堆的特性、堆的抽象数据类型描述)

    目录 优先队列 若采用数组或链表实现优先队列  数组 链表 有序数组 有序链表 总结 若采用二叉搜索树来实现优先队列 最大堆 堆的概念 优先队列的完全二叉树表示 堆的两个特性  结构性 有序性 【例】最大堆和最小堆 【例】不是堆 堆的抽象数据类型描述 优先队列 (Prio

    2024年02月02日
    浏览(52)
  • C语言数据结构初阶(10)----二叉树的实现

    · CSDN的uu们,大家好。这里是C语言数据结构的第十讲。 · 目标:前路坎坷,披荆斩棘,扶摇直上。 · 博客主页: @姬如祎 · 收录专栏: 数据结构与算法     目录 1. 函数接口一览 2. 函数接口的实现 2.1 BTNode* BuyNode(BTDataType x) 的实现 2.2 BTNode* CreateTree() 的实现  2.3 void

    2023年04月08日
    浏览(43)
  • Leetcode刷题---C语言实现初阶数据结构---单链表

    删除链表中等于给定值 val 的所有节点 给你一个链表的头节点head和一个整数val,请你删除链表中所有满足Node.val==val的节点,并返回新的头节点 输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5] 示例 2: 输入:head = [ ], val = 1 输出:[ ] 示例 3: 输入:head = [7,7,7,7], val = 7 输出:

    2024年02月15日
    浏览(56)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包