1. 程式人生 > >最大(小)堆---陣列的實現

最大(小)堆---陣列的實現

最大堆是我在看浙大陳越老師的資料結構視訊學到的,用的陣列實現的

如何描述堆呢?用我自己的話說就是:

根是這顆樹最大的值,每個   根節點都比    左右子節點的值大, 對左右子樹仍然成立;

 那麼最小堆呢正好相反:

根是這顆樹的最小的值, 每個  根節點都比左右子節點的值小, 同樣對左右子樹成立;

圖能最直觀的體現:


我們很容易看出來,堆的左右子樹仍然是堆。

(圖摘自CSDN)所以無論對於最大堆,最小堆,同一層之間的大小順序是不確定的

那麼現在說,如何建立一顆這樣的堆呢?

1、在說堆之前相必大家都瞭解完全二叉樹,以及陣列下標(從1開始)和完全二叉樹節點位置的技巧(即假如說根節點是i,那麼它的左兒子=2*i, 右兒子=2*i+1),堆就是利用完全二叉樹建立的,這裡不理解沒關係,因為答案在下邊。

現在,我們說假如說有這麼一棵樹:

                                                    

假如說現在在右下角插入100,那麼,很明顯就不符合堆的概念了,怎麼調整呢?

如果    它比根節點大,就讓它和根節點調換位置

那麼這顆樹如果是上面大根堆裡面的一顆子樹呢?

還是:如果    它比根節點大,就讓它和根節點調換位置

直到調換到整棵樹  根的位置,或者現在已經比根節點要小了
(已經滿足堆的概念了)

當然最小堆道理是相同的

以上是  當前一顆最大堆,如何往裡面插值,那麼現在我最想知道的是如何構建一個最大堆,你跟我扯插入扯這麼半天干什麼呢?那麼如果咱們是把一組資料往一個空堆裡面插呢?哈哈哈

2、咱們繼續往下說:

如果現在給你一個最大堆:

                                                    

刪除堆的最大值:你怎麼辦呢?

把堆頂刪了(也就是根),為了保證他還是一顆完全二叉樹,就把最下面一層最右邊一個放到堆頂。

為了要讓它還滿足堆的要求,那麼就跟它的左右子節點中最大的交換值(就是說現在80在樹頂,10為左兒子)

現在目標轉移到左子樹即:

                                         

重複操作。

也就是實現了資料的刪操作

那麼我又是在扯皮了,建樹呢?建樹呢?!建樹呢?!!!

3、現在正式來說一下建樹的問題(。。。你終於說到建樹了):

現在給你一組資料,如果你直接放到數組裡面是這個樣子(從下標1開始):

根據陣列下標與完全二叉樹節點的關係,構成的二叉樹是這個樣子:

                                                      

我們知道刪除堆頂時把最下面的值覆蓋堆頂元素,經過操作讓能成為堆,是因為它的左右子樹仍然是堆。

那麼我們現在對26這個節點做如下調整:

~~1、如果     它比左右子節點的最大值小,  就跟較大子節點交換值

~~2、目標移到交換的子節點

~~3、否則滿足堆的概念

然後對6節點做上面的~~ 1   ~~2  ~~3 調整,

然後對15節點做~~ 1   ~~2  ~~3 調整。。。

最後就行成如下堆:

                                                  

over,以下是程式碼實現:

#include<cstdio>
#include<iostream>
#include<cstdlib>

#define MAXDATA 100000000
#define MAXSIZE 10005

using namespace std;

typedef struct HeapChain
{
    int *Element;
    int sizee;
    int capacity;
} HeapChain, *HeapChain_idx;

HeapChain_idx H;
///建造一個空的堆
void Creat_Heap(HeapChain_idx &H)
{
    H = (HeapChain_idx) malloc(sizeof(HeapChain));
    H->Element = (int *)malloc(sizeof(MAXSIZE * sizeof(int)));
    H->Element[0] = MAXDATA;   ///Element[0]的位置做哨兵,防止插入時越界

    H->sizee = 0;
    H->capacity = MAXSIZE - 3;

    return ;
}
int In_Heap(HeapChain_idx H, int x)
{
    if(H->sizee >= H->capacity)
        return 0;

    int i = ++H->sizee;
    for( ; H->Element[i/2] < x; i /= 2)     ///插入的數只要比父節點大,父節點的值就往下移
        H->Element[i] = H->Element[i/2];
    H->Element[i] = x;
    return 1;
}

int Remove_Heap(HeapChain_idx H, int &e)
{
    if(H->sizee == 0)
        return 0;
    e = H->Element[1];
    H->Element[1] = H->Element[H->sizee--];    ///用最後一個數將堆頂覆蓋

    int parent, child;
    for(parent = 1; parent * 2 <= H->sizee; parent = child)      ///將堆頂數往滿足堆定義的位置移動
    {
        child = parent * 2;
        if(child < H->sizee && H->Element[child] < H->Element[child+1])      ///找出左右子節點的較大者
            child++;

        if(H->Element[parent] < H->Element[child])                           ///如果比子節點最大者小就往下移動
        {
            int t;
            t = H->Element[parent], H->Element[parent] = H->Element[child], H->Element[child] = t;
        }
        else                                                                  ///否則就比兩個子節點都大或相等,滿足堆定義。調整完成
            break;
    }

    return 1;
}

int Adjust_Heap(HeapChain_idx H)
{
    if(H->sizee == 0)
        return 0;

    for(int k = H->sizee/2; k > 0; k--)             ///從最後一個節點的父節點調整使滿足堆定義,調整至堆頂
    {
        int parent, child;
        for(parent = k; parent * 2 <= H->sizee; parent = child)   ///如果當前節點要比最大的子節點要小,就往下移,相等不用移動
        {
            child = parent * 2;
            if(child < H->sizee && H->Element[child] < H->Element[child+1])
                child++;

            if(H->Element[parent] < H->Element[child])
            {
                int t;
                t = H->Element[parent], H->Element[parent] = H->Element[child], H->Element[child] = t;
            }
            else
                break;
        }
    }

    return 1;
}


int main()
{
    int n, e;
    HeapChain_idx H;

///測試建堆
    scanf("%d", &n);
    Creat_Heap(H);
    for(int i = 1; i <= n; i++)
        scanf("%d", &H->Element[i]);
    H->sizee = n;
    int flag;
    flag = Adjust_Heap(H);
    if(flag == 0)
        printf("堆為空\n");
    else
        for(int i = 1; i <= H->sizee; i++)
            printf("%d ", H->Element[i]);
    printf("\n\n\n\n");

///測試刪除的堆頂元素:
    flag = Remove_Heap(H, e);
    printf("被刪除的數字為:%d\n", e);
    if(flag == 0)
        printf("堆為空\n");
    else
        for(int i = 1; i <= H->sizee; i++)
            printf("%d ", H->Element[i]);
    printf("\n\n\n\n");

///測試插入一個數:
    printf("請輸入要插入的數字\n");
    scanf("%d", &e);
    flag = In_Heap(H, e);
    if(flag == 0)
        printf("堆已經滿了\n");
    else
        for(int i = 1; i <= H->sizee; i++)
            printf("%d ", H->Element[i]);
    printf("\n\n\n\n");


    return 0;
}