堆的相关操作

要对堆进行操作首先应该清楚什么是堆?

堆:是计算机科学中一类特殊的数据结构的统称,堆通常是一个可以被看做一棵树的数组对象。

堆的性质:

  • 堆的某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树

堆有大堆小堆之分,根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。

堆的定义如下:

n个元素的序列{k1,k2,...,kn}当且仅当满足以下关系时,称之为堆。

(ki <= k2i,ki <= k2i+1)或者(ki >= k2i,ki >= k2i+1),(i = 1,2,3,...n/2)

heap.c文件

#pragma once
#include<stddef.h>
#define HeapMaxSize 1024
typedef char HeapType;

//如果a和b满足比较关系,返回1
//如果a和b不满足比较关系,返回0
//所谓的比较关系,对于小堆来说就是a<b
//对于大堆来说,就是a>b
typedef int (*Compare)(HeapType a,HeapType b);

typedef struct Heap
{
    HeapType data[HeapMaxSize];
    size_t size;
    Compare cmp;
}Heap;

1.堆的初始化

(1)实现函数:

int Less(HeapType a,HeapType b)//为小堆打造的比较函数
{
    return a < b ? 1 : 0;
}

int Greater(HeapType a,HeapType b)
//为大堆打造的比较函数
{
    return a > b ? 1 : 0;
}
void HeapInit(Heap* heap,Compare cmp)
{
    if(heap == NULL)
    {
        //非法输入
        return;
    }
    heap->size = 0;
    heap->cmp = cmp;
    return;
}

(2)测试代码:

void TestInit()
{
    TEST_HEADER;
    Heap heap;
    HeapInit(&heap,Greater);
    printf("size expected 0,actual %lu\n",heap.size);
    printf("cmp expected %p,actual %p\n",Greater,heap.cmp);
}

(3)运行结果:


2.销毁堆

(1)实现代码:

void HeapDestroy(Heap* heap)
{
    if(heap == NULL)
    {
        //非法输入
        return;
    }
    heap->size = 0;
    heap->cmp = NULL;
    return;
}

(2)测试代码:

void TestDestroy()
{
    TEST_HEADER;
    Heap heap;
    HeapDestroy(&heap);
    printf("size expected 0,actual %lu\n",heap.size);
    printf("cmp expected NULL,actual %p\n",heap.cmp);
}

(3)运行结果:


3.向堆中插入元素

思想:先将要插入的元素插入到堆的最后一个位置上然后进行上浮式调整

(1)实现代码:

void Swap(HeapType* a,HeapType* b)
{
    HeapType tmp = *a;
    *a = *b;
    *b = tmp;
    return;
}
void AdjustUp(Heap* heap,size_t index)
{
    if(heap == NULL)
    {
        return;
    }
    //一个变量只做一件事
    size_t child = index;
    size_t parent = (child -1)/2;
    while(child > 0)
    {
        if(!heap->cmp(heap->data[parent],heap->data[child]))
        {
            Swap(&heap->data[parent],&heap->data[child]);
        }
        else
        {
            //如果发现某个位置下,child 和 parent 已经满足堆的要求
            //此时就可以停止上浮
            //因为更上面的节点一定也是满足堆的要求的
            break;
        }
        child = parent;
        parent = (child -1)/2;
    }
    return;
}
void HeapInsert(Heap* heap,HeapType value)
{
    if(heap == NULL)
    {
        //非法输入
        return;
    }
    if(heap->size >= HeapMaxSize)
    {
        //堆已经满了,无法再插入
        return;
    }
    heap->data[heap->size++] = value;
    //对这个堆进行上浮调整
    //调整的起始位置是size-1
    AdjustUp(heap,heap->size - 1);
    return;
}

(2)测试代码:

void HeapPrintChar(Heap* heap,const char* msg)
{
    printf("[%s]\n",msg);
    size_t i = 0;
    for(;i < heap->size; ++i)
    {
        printf("[%c | %lu] ",heap->data[i],i);
    }
    printf("\n");
}
void TestInsert()
{
    TEST_HEADER;
    Heap heap;
    HeapInit(&heap,Greater);
    HeapInsert(&heap,'c');
    HeapInsert(&heap,'b');
    HeapInsert(&heap,'a');
    HeapInsert(&heap,'e');
    HeapInsert(&heap,'f');
    HeapInsert(&heap,'d');
    HeapPrintChar(&heap,"给堆中插入6个元素");
}

(3)运行结果:


4.取堆顶元素

(1)实现代码:

int HeapRoot(Heap* heap,HeapType* value)
{
    if(heap == NULL || value == NULL)
    {
        //非法输入
        return 0;
    }
    if(heap->size == 0)
    {
        //空堆
        return 0;
    }
    *value = heap->data[0];
    return 1;
}

(2)测试代码:

void TestRoot()
{
    TEST_HEADER;
    Heap heap;
    HeapInit(&heap,Greater);
    HeapInsert(&heap,'c');
    HeapInsert(&heap,'b');
    HeapInsert(&heap,'a');
    HeapInsert(&heap,'e');
    HeapInsert(&heap,'f');
    HeapInsert(&heap,'d');
    HeapType value;
    int ret = HeapRoot(&heap,&value);
    printf("ret expected 1,actual %d\n",ret);
    printf("value expected f,actual %c\n",value);
}

(3)运行结果:


5.删除堆顶的元素

思想:现将堆顶元素和堆的最后一个元素进行交换,然后删掉最后一个元素(即此时已将堆顶元素删除,但是可能并不满足堆的要求),所以对堆进行下沉式调整

(1)实现代码:

void AdjustDown(Heap* heap,size_t index)
{
    if(heap == NULL)
    {
        return;
    }
    if(heap->size == 0)
    {
        return;
    }
    size_t parent = index;
    size_t child = 2 * index + 1;
    while(child < heap->size)
    {
        if(child+1 < heap->size && heap->cmp(heap->data[child+1],heap->data[child]))
        {
            //如果右子树存在,并且右子树比左子树更符合堆的要求
            //假设我们这是个小堆,就要求说
            //如果右子树比左子树小,那么就让child指向右子树
            child = child + 1;
        }
        //child 就指向了左右子树中更小的那个元素
        if(heap->cmp(heap->data[child],heap->data[parent]))
        {
            Swap(&heap->data[parent],&heap->data[child]);
        }
        parent = child;
        child = 2 * parent + 1;
    }
}
void HeapErase(Heap* heap)
{
    if(heap == NULL)
    {
        //非法输入
        return;
    }
    if(heap->size == 0)
    {
        //空堆
        return;
    }
    //交换堆顶元素和最后一个元素
    Swap(&heap->data[0],&heap->data[heap->size -1]);
    //进行尾删
    --heap->size;
    //从根节点出发,进行下沉调整
    AdjustDown(heap,0);
    return;
}

(2)测试代码:

void TestErase()
{
    TEST_HEADER;
    Heap heap;
    HeapInit(&heap,Greater);
    HeapInsert(&heap,'c');
    HeapInsert(&heap,'b');
    HeapInsert(&heap,'a');
    HeapInsert(&heap,'e');
    HeapInsert(&heap,'f');
    HeapInsert(&heap,'d');
    HeapErase(&heap);
    HeapPrintChar(&heap,"删除堆顶元素");
}

(3)运行结果:


6.给定一个数组创建一个堆

(1)实现代码:

void HeapCreate(Heap* heap,HeapType array[],size_t size)
{
    if(heap == NULL)
    {
        return;
    }
    //遍历array 数组,把数组的元素依次的插入到堆中
    size_t i = 0;
    for(;i < size;++i)
    {
        HeapInsert(heap,array[i]);
    }
    return;
}

(2)测试代码:

void TestCreate()
{
    TEST_HEADER;
    Heap heap;
    HeapInit(&heap,Greater);
    HeapType array[] = {'d','e','c','a','b'};
    HeapCreate(&heap,array,sizeof(array)/sizeof(array[0]));
    HeapPrintChar(&heap,"创建一个堆");
}

(3)运行结果:


7.运用堆来给一个数组进行排序

思想:现将所给的数组建造成一个堆,然后再对堆进行堆顶元素的删除

如果需要升序排序就要建一个大堆:

因为大堆的堆顶元素为数组中最大的值,每次删除堆顶元素就是将当前数组中的最大值放在了数组的末尾,从而达到了升序排序

如果要进行降序排序,就要建一个小堆:

因为小堆的堆顶元素为数组中最小的值,每次删除堆顶元素就是将当前数组中的最小值放在了数组的末尾,从而达到了降序排序

(1)实现代码:

//如果要进行升序排序,那就要构建一个大堆
//如果要进行降序排序,那就要构建一个小堆
void HeapSort(HeapType array[],size_t size)
{
    //把这个数组构建成一个堆
    Heap heap;
    HeapInit(&heap,Greater);
    HeapCreate(&heap,array,size);
    //循环的堆进行删除操作
    while(heap.size > 0)
    {
        HeapErase(&heap);
    }
    //循环结束后,堆排序就完成了
    memcpy(array,heap.data,size * sizeof(HeapType));
    return;
}

(2)测试代码:

void TestSort()
{
    TEST_HEADER;
    HeapType array[] = {'d','e','c','a','b'};
    HeapSort(array,sizeof(array)/sizeof(array[0]));
    size_t i = 0;
    for(;i < sizeof(array);++i)
    {
        printf("[%c] ",array[i]);
    }
    printf("\n");
}

(3)运行结果:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章