堆的相關操作

要對堆進行操作首先應該清楚什麼是堆?

堆:是計算機科學中一類特殊的數據結構的統稱,堆通常是一個可以被看做一棵樹的數組對象。

堆的性質:

  • 堆的某個節點的值總是不大於或不小於其父節點的值;
  • 堆總是一棵完全二叉樹

堆有大堆小堆之分,根節點最大的堆叫做最大堆或大根堆,根節點最小的堆叫做最小堆或小根堆。常見的堆有二叉堆、斐波那契堆等。

堆的定義如下:

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)運行結果:


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