数据结构之堆(优先级队列)
Posted 玄鸟轩墨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构之堆(优先级队列)相关的知识,希望对你有一定的参考价值。
写在前面
数据结构很久没有更新了,这些天一直在作二叉树的的题目,对我来说有些难,心里面感觉有些烦躁,就一直没有很大家分享知识。今天要说的是关于堆的内容,知识点不多。我们为了更好的理解,我通过C语言来模拟实现它的基础逻辑,由于在Java中封装了这个数据构,堆的叫做优先级队列,我一并说一说它的用法,并且看看IEDA的部分源码。总体而言,还是有一定的难度的,但我相信,如此帅的读者一定会掌握的。
初始堆
谈到堆就不的提一下二叉树,在众多二叉树中,有一个最基本的,也是很重要的叫做完全二叉树,完全二叉树可以理解为从根节点开始标号,从0开始,每一层从左往有按照树的节点依次标记,不会出现节点跳跃的树。
我们都是知道,树是一个非线性的结构,它数据存储的空间并不连续,我们是否可以有一个做法使得完全二叉树中的数据集中起来。堆就是这个结构。
- 堆的物理结构是一个数组
- 堆的逻辑结构是一个完全二叉树
堆如何存储树中的数据
堆的存储数据方式是层序遍历,我们一层一层的将数据放到数组中。不过我们这里不会说数的遍历,这里今天谈二叉树就是我了辅助理解我们今天的堆,辅助我们如何建立堆。
堆的性质
堆有很多优良特性,在谈之前,我们先说说堆的分类
堆分为两种
- 大堆 (大根堆) 双亲节点存储的数据大于孩子节点存储的数据
- 小堆 (小根堆) 双亲节点存储的数据小于孩子节点存储的数据
关于堆的特性我们也要谈一谈,如果我们把根节点的下标标为 0 ,要是我们给出一个下标为 i,的节点,我们就可以得到它的双亲结点和孩子节点的下标(要是存在的话)
- 双亲结点 (i - 1) / 2
- 左孩子 2 × i + 1 右孩子 2 × i + 2
建堆
理论知识我们需要了解这么多就可以了,下面开始的都是我们的实际操作。我们以建小堆为例吧,只要学会了小堆,大堆也不在话下了。
我们需要一个结构体来辅助我们完成任务,一起看看吧
typedef int HPDataTytpe;
struct Heap
HPDataTytpe* elem; // 指针
size_t szie; // 数组的有限元素的个数
size_t capacity; // 数组的空间大小
;
我们要写下面几个函数,明白了这些函数的原理,我们就可以掌握了堆了
//初始化 堆
void HeapInit(Heap* php);
//销毁 堆
void HeapDestroy(Heap* php);
//数据入堆
void HeapPush(Heap* php, HPDataTytpe val);
//数据出堆
void HeapPop(Heap* php);
//判断堆是不是 空
bool HeapEmpty(Heap* php);
//向上调整
void adjustUp(HPDataTytpe* elem, int size);
//向下调整
void adjustDown(HPDataTytpe* elem, int size,size_t root);
初始化堆&销毁堆
这个很简单,我们就不说了,我把代码放在下面了。
void HeapInit(Heap* php)
assert(php);
php->elem = NULL;
php->capacity = 0;
php->szie = 0;
void HeapDestroy(Heap* php)
assert(php);
free(php->elem);
php->elem = NULL;
php->capacity = 0;
php->szie = 0;
数据入堆
这个才是我们今天的硬菜之一,最好吃的就是里面的向上调整,对于void HeapPush(Heap* php, HPDataTytpe val);函数,我们把数据放在数组的最后面,不过这个数组还一定是小堆吗?不一定的,我们需要进行向上调整才能让它仍旧保持成一个小堆。
void HeapPush(Heap* php, HPDataTytpe val)
assert(php);
//判满
if (php->capacity == php->szie)
size_t newSize = (php->capacity == 0) ? 4 : 2 * (php->capacity);
HPDataTytpe* pCur = (HPDataTytpe*)realloc(php->elem, sizeof(HPDataTytpe)*newSize);
assert(pCur);
php->elem = pCur;
php->capacity = newSize;
php->elem[php->szie++] = val;
adjustUp(php->elem, php->szie); // 向上调整
向上调整
当我们数据进入数组后,不一定还是一个堆了,所以我们要通过向上调整使得它仍旧保持一个堆,下面是向上调整的原理图
void swap(HPDataTytpe* pa, HPDataTytpe*pb)
assert(pa&&pb);
HPDataTytpe ret = *pa;
*pa = *pb;
*pb = ret;
void adjustUp(HPDataTytpe* elem, int size)
assert(elem);
int child = size - 1;
int parent = (child - 1) / 2;
while (child > 0) //判断条件很重要
if (elem[child] < elem[parent])
swap(&elem[child], &elem[parent]); // 交换 这两个数
else
break;
child = parent;
parent = (child - 1) / 2;
通过上面我们就可以建立一个小堆了,用代码试试
int main()
Heap heap;
HeapInit(&heap);
HeapPush(&heap, 3);
HeapPush(&heap, 5);
HeapPush(&heap, 6);
HeapPush(&heap, 10);
HeapPush(&heap, 0);
for (int i = 0; i < (int)heap.szie; i++)
printf("%d ", heap.elem[数据结构之堆(优先级队列)