二叉堆與堆排序總結


前言

堆的數據結構是一個完全二叉樹,其能實現優先隊列的功能,主要的作用是能夠動態地維護一個有關優先級的序列,使其在插入和刪除都能在O(logn)O(logn)時間內完成,查詢能在O(1)O(1)完成。


性質

對於小根堆而言,這個二叉樹形的結構只要滿足所有的父節點都小於子節點就是小根堆,顯然,這是個遞歸的定義。對於大根堆則是父節點大於子節點。


抽象

對於要操作一個二叉堆我們要實現以下幾個接口:

  • shift_Down:即下沉的操作,這個接口的功能是當讓某個位置的元素下沉,使其這個位置爲根的堆仍滿足二叉堆的定義
  • shift_Up:即上浮的操作,同理,讓這個元素所屬的樹都滿足二叉堆的定義,即如果是小根堆,父節點比該位置的結點大則要交換兩個值,然後繼續向上探測
  • isEmpty:判斷堆是否爲空,即判斷tot是否爲0
  • push:插入的操作,具體做法有三步:一,將結點加入最後使得二叉堆仍是一個完全二叉樹的形狀;二,結點數tot個數+1;三,將新插入的這個數上浮使得整個堆仍具有原來的性質
  • pop:刪除的操作,具體做法有三步:一,將最後一個元素替代第一個元素,使得二叉堆仍是一個完全二叉樹的形狀;二,結點數tot個數-1;三,將堆頂的這個數下沉使得整個堆仍具有原來的性質
  • top:查詢的操作,就是查堆頂元素即第一個元素,如果是小根堆就是最小的元素。
  • heapify:構建堆的操作,比較暴力的做法是加入對一個空堆插入n個結點,但複雜度顯然是O(nlogn)O(nlogn)的,不能讓我們滿意,有個O(n)O(n)的做法就是讓最後一個非葉子結點的結點開始遞歸地構建自己地子堆,直到第一個元素,這樣整個堆也就符合堆的定義了。至於爲什麼是O(n)O(n)的可以百度一下,這裏不是重點。關於爲什麼要從(tot1)/2(tot-1)/2開始往前做是因爲這個位置的結點一定有孩子結點並且是最後一個,因爲大於它的結點通過加1乘2的數字顯然會大於等於tot
  • heap_sort:堆排序,首先交換堆首和堆尾,之後堆大小-1,接着把交換的那個數下沉。做n-1次得出來的序列就是一個從大到小(從小到大)的序列。複雜度O(nlogn)O(nlogn)

實現

#include <bits/stdc++.h>
using namespace std;
#define ElemType int
#define MAXN 1005

void swap(ElemType& a, ElemType& b) {
    ElemType tmp = a;
    a = b;
    b = tmp;
}

//堆是否爲空
bool isEmpty(ElemType heap[], int& tot) {
    return tot <= 0 ? true : false;
}

//上浮,先記憶我們要上浮的值,找到空位後再把這個值填上去
void shift_Up(ElemType heap[], int& tot, int index) {
    int fa, tmp = heap[index];
    while(index) { //如果index本身是根就沒有上浮的必要
        fa = index - 1 >> 1; //找index的父親結點
        if(tmp < heap[fa]) heap[index] = heap[fa];
        else break;
        index = fa;
    }
    heap[index] = tmp;
}

//下沉,先記憶我們要下沉的值,找到空位後再把這個值填上去
void shift_Down(ElemType heap[], int& tot, int index) {
    int son, tmp = heap[index];
    while((index << 1 | 1) < tot) { //如果index沒有左孩子就沒有下沉的必要
        son = index << 1 | 1; //左孩子 
        if (son+1 < tot && heap[son+1] < heap[son]) son++;  //如果右孩子存在並且比左孩子小就取它
        if (heap[son] >= tmp) break; //此時該位置就是我們要填的
        else heap[index] = heap[son];
        index = son; //下沉
    }
    heap[index] = tmp; //找到那個位置填下去
}

//插入
bool push(ElemType heap[], int& tot, int val) {
    if(tot >= MAXN) return false;
    heap[tot++] = val;
    shift_Up(heap, tot, tot-1); //把新插入的結點上浮
    return true;
}

//刪除
bool pop(ElemType heap[], int& tot) {
    if (isEmpty(heap, tot)) return false;
    swap(heap[0], heap[--tot]);
    shift_Down(heap, tot, 0); //把新的根下沉
    return true;
}

//查詢
bool top(ElemType heap[], int& tot, ElemType& val) {
    if(isEmpty(heap, tot)) return false; //查詢失敗    
    val = heap[0];
    return true;;
}

//構建
void heapify(ElemType heap[], int& tot) {
    for(int i=(tot-1)/2; i>=0; i--)
        shift_Down(heap, tot, i);
}

//堆排序
void heap_sort(ElemType heap[], int& tot) {
    for(int i=tot-1; i>0; i--) {
        swap(heap[0], heap[i]);
        shift_Down(heap, i, 0);
    }
}

////////////////遞歸下沉///////////////////

void shift_Down2(ElemType heap[], int tot, int CurRootIndex) {
    if(CurRootIndex < tot) {
        int fa = CurRootIndex; //存父節點
        int left = CurRootIndex << 1 | 1;
        int right = left + 1;
        if (left < tot && heap[CurRootIndex] > heap[left])
            CurRootIndex = left;
        if (right < tot && heap[CurRootIndex] > heap[right])
            CurRootIndex = right;
        if (CurRootIndex ^ fa) { //存在下移的可能
            ElemType tmp = heap[CurRootIndex];
            heap[CurRootIndex] = heap[fa];
            heap[fa] = tmp;
            shift_Down2(heap, tot, CurRootIndex); //遞歸建堆
        }
    }
}

//構建2
void heapify2(ElemType heap[], int& tot) {
    for(int i=(tot-1)/2; i>=0; i--)
        shift_Down2(heap, tot, i);
}

//////////////////////////////////////////

int main() {
    int val;
    int arr[MAXN]={10,9,8,7,6,5,4,3,2,1}, tot=10;

    heapify(arr, tot); 

    for(int i=0; i<tot; i++) cout << arr[i] <<' ' ; cout << endl;
    
    if( top(arr, tot, val) == true) {
        cout << "val = " << val << '\n';
        cout << "tot = " << tot << '\n';
    }

    if( pop(arr, tot) == true) {
        if( top(arr, tot, val) == true) {
            cout << "val = " << val << '\n';
            cout << "tot = " << tot << '\n';
        }
    }

    if( push(arr, tot, 4) == true) {
        for(int i=0; i<tot; i++) cout << arr[i] <<' ' ; cout << endl;
        if( top(arr, tot, val) == true) {
            cout << "val = " << val << '\n';
            cout << "tot = " << tot << '\n';
        }
    }

    heap_sort(arr, tot);

    for(int i=0; i<tot; i++) cout << arr[i] <<' ' ; cout << endl;

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