數據結構---堆的基本操作及堆排序

介紹

首先我們要將數據結構中的堆和內存中的堆區區分開來,內存中的堆區是操作系統管理的,和數據結構中的堆沒有半毛錢關係。堆只有兩種,大堆和小堆。

  • 大堆(大根堆):父節點的值大於左右孩子結點的值,也就是說大堆的根節點是整個堆中的最大值,左右孩子結點的值毫無關係
  • 小堆(小根堆):父節點的值小於左右孩子結點的值,也就是說小堆的根節點是整個堆中的最小值,左右孩子結點的值毫無關係

這裏寫圖片描述

堆的表示

我們通常用一個數組來表示一個堆,數組中存放的是堆的層序遍歷結果,第一個元素即根節點
這裏寫圖片描述

實現堆的基本操作

  • 結構體聲明
//定義一個比較函數的函數指針,用來指明該堆是小堆還是大堆
typedef int (*Compare)(HeapType a, HeapType b); 

typedef struct Heap{
    HeapType data[HeapMaxSize];//數組表示,存放堆中數據
    size_t size;//表示有效數據的個數
    Compare com;//函數指針,用來決定實現的是小堆還是大堆
}Heap;
  • 比較函數

//初始化的時候將堆初始化成大堆或者小堆(傳函數名進去),在進行插入刪除操作的時候用到該函數
int Greater(HeapType a, HeapType b)//表示大堆
{
    return a > b?1:0;
}

int Less(HeapType a, HeapType b)//表示小堆
{
    return a < b?1:0;
}
  • 堆的初始化和銷燬
//初始化堆
void HeapInit(Heap* heap,Compare cmp)
{
    if(heap == NULL)
    {
        //非法輸入
        return;
    }
    heap->size = 0;
    heap->com = cmp;
}
//銷燬堆
void HeapDestroy(Heap* heap)
{
    if(heap == NULL)
    {
        return;
    }
    heap->size = 0;
}

  • 堆的插入和刪除堆頂元素(堆主要是使用其堆頂元素)操作
    • 堆的插入中最重要的就是上浮操作,意思是每次插入新數據時先放置到數組的末尾,然後和其父節點比較,這時就要用到比較函數,如果滿足函數,就不動,不滿足就要進行上移,也就是數據交換,直到移動到它合適的位置
    • 堆的刪除中最重要的就是下潛操作,先將堆頂元素,也就是0號下標元素和數組末尾元素交換數據,再將堆頂元素按堆的規則(大堆還是小堆)往下移動,直到移動到它合適的位置

可以說 上浮下潛 是堆裏最重要的兩個操作

//交換函數
void Swap(HeapType *a, HeapType *b)
{
    HeapType tmp = *a;
    *a = *b;
    *b = tmp;
}


//上浮函數
void AdjustUp(Heap* heap, size_t child)
{
    if(heap == NULL)
    {
        return;
    }
    if(child == 0)
    {
        return;
    }
    size_t parents = (child - 1)/2;
    if(!heap->com(heap->data[parents],heap->data[child]))
    {
        Swap(&heap->data[parents],&heap->data[child]);
        AdjustUp(heap, parents);
    }else
    {
        return;
    }
}
//插入元素
void HeapInsert(Heap* heap, HeapType value)
{
    if(heap == NULL)
    {
        //非法輸入
        return;
    }
    if(heap->size >= HeapMaxSize)
    {
        //堆滿了
        return;
    }
    heap->data[heap->size++] = value;
    size_t child = heap->size - 1;
    AdjustUp(heap, child);
}
//下潛函數
void AdjustDown(Heap* heap, size_t parents, size_t size)
{                                                                                                                                       
    if(heap == NULL || parents >= size - 1 )
    {
        return;
    }
    size_t child = parents*2 + 1;//先定義其左孩子結點
    while(child > 0 && child < size)//如果左孩子結點存在,進入循環
    {
        //(以大堆爲例)
        //我們是不清楚其左右孩子結點誰大誰小的,要確定出大的那個值才能進行交換
        if(child + 1 < size)//如果存在右孩子結點
        {
            //(以大堆爲例)
            //如果右孩子結點的值大於左孩子結點,child+1就定位到右孩子結點了
            //否則就是左孩子結點
            if(!heap->com(heap->data[child],heap->data[child+1]))
            {
                child += 1;
            }
        }
        //如果父結點的值小於左右孩子結點中的最大值,就要進行交換
        //否則退出循環,表明已找到合適位置
        if(!heap->com(heap->data[parents],heap->data[child]))
        {
            Swap(&heap->data[parents],&heap->data[child]);
            parents = child;
            child = parents*2 + 1;
        }else{
            break;
        }
    }
    return;
}
//刪除堆頂元素
void HeapErase(Heap* heap)
{
    if(heap == NULL)
    {
        //非法輸入
        return;
    }
    if(heap->size <= 0)
    {
        //空堆
        return;
    }
    Swap(&heap->data[heap->size-1], &heap->data[0]);
    heap->size--;
    AdjustDown(heap,0,heap->size);
    return;
}
  • 取堆頂元素太簡單了,就是返回數組下標爲0的值,即根節點,代碼就不粘了。
  • 創建一個堆,即給定一個無序數組,將其實現爲一個堆,遍歷數組,循環的進行插入操作就好了。介紹這個主要是爲了堆排序
//創建堆
void HeapCreate(HeapType array[], Heap* heap,size_t size)
{
    if(heap == NULL || size <= 0)
    {
        return;
    }
    size_t index = 0;
    while(size--)
    {
        HeapInsert(heap,array[index]);
        index++;
    }
}
  • 堆排序
    • 給定一個無序數組,使用堆排序的方式將其有序化,很簡單,我們先把該數組創建成一個堆,我們只能保證父結點的值大於孩子結點的值,不能保證整個數組有序。
    • 這時我們就要用到刪除堆頂元素操作,每次刪除的時候都是把堆頂元素交換到了數組末尾,有效數據-1,而並沒有真正的將其移除,所以我們進行循環的刪除操作,以大堆爲例,每次被放置在數組末尾的元素都是堆中的最大值,所以當堆爲空的時候,整個數組也就變成升序數組了(不考慮size)。最後將其memcpy到原來的數組中即可
    • 注意:升序數組用大堆,降序數組用小堆
//堆排序
void HeapSort(HeapType array[], Heap* heap, size_t size)
{
    if(heap == NULL || size <= 0)
    {
        return;
    }
    size_t num = size;
    HeapCreate(array,heap,size);
    while(size--)
    {
        HeapErase(heap);
    }
    memcpy(array,heap->data,num*sizeof(HeapType));
}
發佈了50 篇原創文章 · 獲贊 26 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章