1. 程式人生 > >資料結構堆的實現以及堆的面試題

資料結構堆的實現以及堆的面試題

堆的特點

堆有大堆和小堆之分

小堆:
①任意結點的關鍵碼均小於等於他的左右孩子的關鍵碼
②位於堆頂的結點的關鍵碼最小
③從根結點到每個結點的路徑上陣列元素組成的序列都是遞增的

大堆:
①任意結點的關鍵碼均大於等於他的左右孩子的關鍵碼
②位於堆頂的結點的關鍵碼最大
③從根結點到每個結點的路徑上陣列元素組成的序列都是遞減的

這裡寫圖片描述

堆儲存在下標為0的陣列中,因此在堆中給定下標為i的結點時有:
①如果i=0,結點i是根節點,沒有雙親節點;否則結點i的雙親結點為
結點(i-1)/2
②如果2 * i + 1 <= n - 1(n為堆中結點個數),則結點i的左孩子為結
點2 * i + 1,否則結點i無左孩子
③如果2 * i + 2 <= n - 1(同上),則結點i的右孩子為結點2 * i + 2,
否則結點i無右孩子

堆的實現

heap.h

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int HPDataType;
typedef int (*PCompare)(HPDataType left, HPDataType right);//函式指標,用小堆就選Less,用大堆就選Greater
typedef struct Heap
{
    HPDataType *_hp;
    int _size;//堆中的元素個數
    int _capacity;//堆的容量
PCompare _com; }Heap; // 小於比較 int Less(HPDataType left, HPDataType right); // 大於比較 int Greater(HPDataType left, HPDataType right); // 初始化堆 void InitHeap(Heap* hp); // 建立堆 void CreateHeap(Heap* hp, int* array, int size, PCompare com); //檢查擴容 void _CheckCapacity(Heap* hp); // 在堆中插入元素data void InsertHeap(Heap* hp, HPDataType data); // 刪除堆頂的元素
void RemoveHeap(Heap* hp); // 獲取堆中有效元素個數 int SizeHeap(Heap* hp); // 檢測堆是否為空 int EmptyHeap(Heap* hp); // 獲取堆頂元素 HPDataType TopHeap(Heap* hp); // 銷燬堆 void DestroyHeap(Heap* hp); // 向下調整 void AdjustDown(Heap* hp, int parent); // 向上調整 void AdjustUp(Heap* hp, int child);
heap.c


#include "heap.h"


int Less(HPDataType left, HPDataType right)
{
    return left < right;
}

int Greater(HPDataType left, HPDataType right)
{
    return left > right;
}

void InitHeap(Heap* hp)
{
    assert(hp);
    hp->_hp = (HPDataType*)malloc(3 * sizeof(HPDataType));//初始化開闢多少都行
    hp->_capacity = 3;
    hp->_size = 0;
}

void Swap(HPDataType *left, HPDataType*right)
{
    HPDataType tmp = 0;
    tmp = *left;
    *left = *right;
    *right = tmp;
}

void AdjustUp(Heap* hp, int child)
{
    assert(hp);
    int parent = (child - 1) / 2;//找出父母結點下標
    while (child)//只要child還為非零,就繼續迴圈
    {
        if (hp->_com(hp->_hp[child], hp->_hp[parent]))//是否需要父母結點
                                                      //與孩子結點交換關鍵碼
        {
            Swap(&hp->_hp[child], &hp->_hp[parent]);
            child = parent;//此時孩子結點上移到父母結點
            parent = (child - 1) / 2;//計算新的父母結點
        }
        else
            return;
    }
}

void AdjustDown(Heap* hp, int parent)
{
    assert(hp);
    if (NULL == hp)
        return;
    int child = parent * 2 + 1;//計算左孩子結點下標
    while (child < hp->_size)//只要孩子結點下標不越界
    {
        if (child + 1 < hp->_size&&hp->_com(hp->_hp[child+1],hp->_hp[child]))
        //是否需要左右孩子交換關鍵碼,我是以左孩子是否與父母交換關鍵碼為前提
        {
            Swap(&hp->_hp[child], &hp->_hp[child + 1]);
        }
        if (hp->_com(hp->_hp[child], hp->_hp[parent]))//是否左孩子與父母交換關鍵碼
        {
            Swap(&hp->_hp[child], &hp->_hp[parent]);
            parent = child;//父母結點移到孩子結點,當做下一個父母結點
            child = parent * 2 + 1;
        }
        else
            return;
    }
}

void CreateHeap(Heap* hp, int* array, int size, PCompare com)
{
    assert(hp);
    int root = (size - 2) / 2;//找到最後一個非葉子結點的結點
    int i = 0;
    hp->_hp = (HPDataType*)malloc(size * sizeof(HPDataType));
    if (hp == NULL)
        return;
    for (i = 0; i < size; i++)
    {
        hp->_hp[i] = array[i];
    }
    hp->_capacity = size;
    hp->_size = size;
    hp->_com = com;
    while (root>=0)//從當前結點一直向下調整到下標為零的結點
    {
        AdjustDown(hp,root);
        root--;
    }
}

void _CheckCapacity(Heap* hp)
{
    assert(hp);
    int i = 0;
    HPDataType *new = NULL;
    int newcapacity = hp->_capacity * 2;
    new = (HPDataType*)malloc(newcapacity * sizeof(HPDataType));
    if (new == NULL)
        return;
    for (i = 0; i < hp->_size; i++)
    {
        new[i] = hp->_hp[i];
    }
    free(hp->_hp);
    hp->_hp = new;
    hp->_capacity = newcapacity;
}

void InsertHeap(Heap* hp, HPDataType data)
{
    assert(hp);
    if (hp->_capacity == hp->_size)
        _CheckCapacity(hp);
    hp->_hp[hp->_size] = data;//一定要先插入後size++
    AdjustUp(hp, hp->_size);
    hp->_size++;
}

void RemoveHeap(Heap* hp)
{
    assert(hp);
    if (NULL == hp)
        return;
    hp->_hp[0] = hp->_hp[hp->_size - 1];
    hp->_size--;//一定要先size--,後向下調整,否則他會把刪除的結點也調整了
    AdjustDown(hp, 0);
}

int SizeHeap(Heap* hp)
{
    assert(hp);
    return hp->_size;
}

int EmptyHeap(Heap* hp)
{
    assert(hp);
    return 0 == hp->_size;
}

HPDataType TopHeap(Heap* hp)
{
    assert(hp);
    return hp->_hp[0];
}

void DestroyHeap(Heap* hp)
{
    assert(hp);
    if (hp)
    {
        free(hp);
        hp->_capacity = 0;
        hp->_size = 0;
    }
}

優先順序佇列

heap.h


typedef struct PriorityQueue
{
    Heap hp;
}PriorityQueue;

//初始化
void InitPriorityQueue(PriorityQueue* q);

//入佇列
void PushPriorityQueue(PriorityQueue* q, HPDataType data);

//出佇列
void PopPriorityQueue(PriorityQueue* q);

//取隊頭元素
void TopPriorityQueue(PriorityQueue* q);

//佇列長度
int SizePriorityQueue(PriorityQueue* q);

//判空
int EmptyPriorityQueue(PriorityQueue* q);
heap.c


void InitPriorityQueue(PriorityQueue* q)
{
    InitHeap(&q->hp);
}

void PushPriorityQueue(PriorityQueue* q, HPDataType data)
{
    InsertHeap(&q->hp, data);
}

void PopPriorityQueue(PriorityQueue* q)
{
    RemoveHeap(&q->hp);
}

void TopPriorityQueue(PriorityQueue* q)
{
    return TopHeap(&q->hp);
}

int SizePriorityQueue(PriorityQueue* q)
{
    return SizeHeap(&q->hp);
}

int EmptyPriorityQueue(PriorityQueue* q)
{
    return EmptyHeap(&q->hp);
}

堆排序(降序,用小堆)

堆排序的原理
①將堆頂元素和堆中最後一個元素交換
②此時陣列最後一個元素已經排好序了,因此將堆中元素減少一個
③此時堆結構可能被破壞,再向下調整使其滿足堆的性質
④將以上三個步驟迴圈起來,直到排序完陣列中的所有元素

void _AdjustDown(int *array, int size, int parent)
{
    int child = parent * 2 + 1;
    while (child < size)
    {
        if (child + 1 < size&&array[child + 1] < array[child])
        {
            Swap(&array[child + 1], &array[child]);
        }
        if (array[parent] > array[child])
        {
            Swap(&array[parent], &array[child]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
            return;
    }
}
void MakeHeap(int *array, int size)
{
    int root = (size - 2) / 2;
    while (root >= 0)
    {
        _AdjustDown(array,size, root);
        root--;
    }
}

void HeapSort(int *array, int size)
{
    MakeHeap(array, size);//把傳過來的陣列建立成堆
    while (size)//調整完所有元素
    {
        Swap(&array[0], &array[size - 1]);//交換堆頂元素和堆中最後一個元素
        size--;//減少堆中元素一個
        _AdjustDown(array, size, 0);//向下調整
    }
}

測試

test.c


#include "heap.h"
int main()
{
    //Heap hp;//被註釋了的是測試堆的實現的程式碼,沒被註釋的是測試堆排序的程式碼,優先順序佇列大家就自己測試吧
    int i = 0;
    int array[] = { 53,17,78,9,45,65,87,23,31 };
    //InitHeap(&hp);
    //CreateHeap(&hp, array, sizeof(array)/sizeof(array[0]), Less);
    //for (i = 0; i < hp._size; i++)
    //{
    //  printf("%d ", hp._hp[i]);
    //}
    //printf("\n");
    //printf("%d ", TopHeap(&hp));
    //printf("%d ", SizeHeap(&hp));
    //printf("\n");
    int size = sizeof(array) / sizeof(array[0]);
    HeapSort(array,size);
    for (i = 0; i < size; i++)
    {
        printf("%d ", array[i]);
    }
    printf("\n");
    //InsertHeap(&hp,11);
    //for (i = 0; i < hp._size; i++)
    //{
    //  printf("%d ", hp._hp[i]);
    //}
    //printf("\n");
    //printf("%d ", TopHeap(&hp));
    //printf("%d ", SizeHeap(&hp));
    //printf("\n");
    //RemoveHeap(&hp);
    //for (i = 0; i < hp._size; i++)
    //{
    //  printf("%d ", hp._hp[i]);
    //}
    //printf("\n");
    //printf("%d ", TopHeap(&hp));
    //printf("%d ", SizeHeap(&hp));
    system("pause");
    return 0;
}