数据结构——堆

简介: 数据结构——堆



一、前言

前面我们学习了二叉树,知道了二叉树一般用链式存储,但是还有一种存储方式是顺序存储,而

通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用

顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储。


二、堆的概念及结构

n个元素的序列{k1,k2,ki,…,kn}当且仅当满足下关系时,称之为堆。K(i) <= K(2i) 且 K(i) <= K(2i+1)称为小堆 或者 K(i) >= K(2i+1)  且 K(i) >= K(2i+1) 称为大堆。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:

*堆中某个节点的值总是不大于或不小于其父节点的值。

* 堆总是一棵完全二叉树。

如下图

大根堆(大堆)

小根堆(小堆)


三、堆的实现

均以小堆为例

* 首先我们先来定义一个堆

typedef int HPDataType
typedef struct Heap
{
    HPDataType* a; //数组
    int size; //元素个数
    int capacity; //容量大小
}HP;

* 接着我们再来实现堆的各种接口函数

void HeapInit(HP* php)
{
    assert(php);
    php->a = NULL;
    php->size = php->capacity = 0;
}
bool HeapEmpty(HP* php)
{
    return php->size = 0;
}
void HeapDestory(HP* php);
{
    assert(php);
    free(php->a);
    php->a = NULL;
    php->size = php->capacity = 0;
}
int HeapTop(HP* php) //取堆顶元素
{
    assert(php);
    assert(php->size > 0);
    return php->a[0];
}
int HeapSize(HP* php) //堆的大小
{
    assert(php);
    return php->size;
}

* 接下来我们就来实现堆的插入数据和删除数据的函数

1、堆的插入:先插入一个数到数组的尾上,再进行向上调整算法,直到满足堆。

向上调整算法:

void swap(int* a, int* b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}
void AdjustUp(HPDatatype* a, int child)
{
    int parent = (child - 1) / 2;
    while(child > 0)
    {
        if(a[child] < a[parent])
        {
            swap(&a[child], &a[parent]);
        }
        else
        {
            break;
        }
        child = parent;
        parent = (child - 1) / 2;
    }
}

插入函数

void HeapPush(HP* php, HPDatatype x)
{
    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)
        {
            printf("realloc fail");
            exit(-1);
        }
        php->a = tmp;
        php->capacity = newcapacity;
    }
    php->a[php->size] = x;
    php->size++;
    AdjustUp(php->a, php->size - 1);
}

2、堆的删除:删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。

向下调整算法:

void AdjustDown(HeapDatatype* a, int size, int parent)
{
    int child = parent * 2 + 1; //默认左孩子最小
    while(child < size)
    {
        if(a[child] > a[child + 1] && child + 1 < size)
        {
            child++;
        }
        if(a[parent] > a[child])
        {
            swap(&a[parent], &a[child]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            break;
        }
    }
}

删除函数

void HeapPop(HP* php) /*删除堆顶的数据,将第一个和最后一个交换,
删除最后一个利用向下调整算法将其重新调整为堆*/
{
    assert(php);
    assert(php->size > 0);
    swap(&php->a[0], &php->a[php->size-1]);
    php->size--;
    AdjustDown(php->a, php->size, 0);
}

四、堆的应用

1、TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决。

基本思路如下:

* 用数据集合中前K个元素来建堆 :前k个最大的元素,则建小堆 ,前k个最小的元素,则建大堆。

* 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素。

* 将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。

void PrintTopK(int* a, int n, int k)
{
    //1、建堆——用a中前k个元素建堆
    int* kMinHeap = (int*)malloc(sizeof(int) * k);
    assert(kMinHeap);
    for(int i = 0; i < k; i++)
    {
        kMinHeap[i] = a[i];
    }
    for(int i = (k-1-1) / 2; i>=0; i--)
    {
        AdjustDown(kMinHeap, k, i);
    }
    //2、将剩余n-k个元素依次与堆顶元素比较并交换
    for(int j = k; j < n; j++)
    {
        if(kMinHeap[0] < a[j])
        {
            kMinHeap[0] = a[j];
            AdjustDown(kMinHeap, k, 0);
        }
    }
    for (int i = 0; i < k; i++)
  {
    printf("%d ", kMinHeap[i]);
  }
}

2、堆还有一个应用就是应用在堆排序中,堆排序即利用堆的思想来进行排序。这里我们不做讲解,而会在排序中专门讲解。

目录
相关文章
|
7天前
|
存储 算法
【数据结构】堆
【数据结构】堆
|
1月前
|
算法 安全 大数据
揭秘!Python堆与优先队列:数据结构的秘密武器,让你的代码秒变高效战士!
【7月更文挑战第8天】Python的heapq模块和queue.PriorityQueue提供堆与优先队列功能,助你提升算法效率。堆用于快速找大数据集的第K大元素,如示例所示,时间复杂度O(n log k)。PriorityQueue在多线程中智能调度任务,如模拟下载管理器,按优先级处理任务。掌握这些工具,让代码运行更高效!
49 1
|
7天前
|
存储 算法 Linux
【数据结构】树、二叉树与堆(长期维护)(1)
【数据结构】树、二叉树与堆(长期维护)(1)
|
12天前
|
搜索推荐 算法 Go
深入探索堆:Go语言中的高效数据结构
深入探索堆:Go语言中的高效数据结构
|
7天前
|
算法
【数据结构】树、二叉树与堆(长期维护)(2)
【数据结构】树、二叉树与堆(长期维护)(2)
【数据结构】树、二叉树与堆(长期维护)(2)
|
12天前
【数据结构】二叉树顺序实现(大堆)-->赋源码
【数据结构】二叉树顺序实现(大堆)-->赋源码
26 4
|
1月前
|
存储 算法
【数据结构】堆,堆的实现,堆排序,TOP-K问题
数据结构——堆,堆的实现,堆排序,TOP-K问题
22 1
【数据结构】堆,堆的实现,堆排序,TOP-K问题
|
2天前
|
算法
【初阶数据结构篇】堆的应用(堆排序与Top-K问题)
即求数据结合中前K个最⼤的元素或者最⼩的元素,⼀般情况下数据量都⽐较⼤。
|
2天前
|
存储 算法 测试技术
【初阶数据结构篇】实现顺序结构二叉树(堆的实现方法)
注意传过去的参数是插入的位置,即插入前的size,在调整完后再将size++
|
5天前
|
存储
全局变量和局部变量在堆和栈的区别
全局变量和局部变量在堆和栈的区别
18 0