03——堆、堆排序(O(nlogm))

1.二叉堆的定義(最大堆)

在這裏插入圖片描述

2.二叉堆的存儲

在這裏插入圖片描述

3.操作

3.1 插入元素(shift up)

插入前:

在這裏插入圖片描述

插入後:

在這裏插入圖片描述

3.2 取出元素(shift down)

取出前:

在這裏插入圖片描述

在這裏插入圖片描述

調整後:

在這裏插入圖片描述

4.堆的實現

template<typename Item>
class MaxHeap{

private:
    Item *data;
    int count;
    int capacity;

    void shiftUp(int k){
        while( k > 1 && data[k/2] < data[k] ){
            swap( data[k/2], data[k] );
            k /= 2;
        }
    }

    void shiftDown(int k){
        while( 2*k <= count ){
            int j = 2*k; // 在此輪循環中,data[k]和data[j]交換位置
            if( j+1 <= count && data[j+1] > data[j] )
                j ++;
            // data[j] 是 data[2*k]和data[2*k+1]中的最大值

            if( data[k] >= data[j] ) break;
            swap( data[k] , data[j] );
            k = j;
        }
    }

public:
    // 構造函數, 構造一個空堆, 可容納capacity個元素
    MaxHeap(int capacity){
        data = new Item[capacity+1];
        count = 0;
        this->capacity = capacity;
    }

 	// 構造函數, 通過一個給定數組創建一個最大堆
    // 該構造堆的過程, 時間複雜度爲O(n)
    MaxHeap(Item arr[], int n){
        data = new Item[n+1];
        capacity = n;

        for( int i = 0 ; i < n ; i ++ )
            data[i+1] = arr[i];
        count = n;

        for( int i = count/2 ; i >= 1 ; i -- )
            shiftDown(i);
    }

    ~MaxHeap(){
        delete[] data;
    }

    // 返回堆中的元素個數
    int size(){
        return count;
    }

    // 返回一個布爾值, 表示堆中是否爲空
    bool isEmpty(){
        return count == 0;
    }

    // 像最大堆中插入一個新的元素 item
    void insert(Item item){
        assert( count + 1 <= capacity );
        data[count+1] = item;
        shiftUp(count+1);
        count ++;
    }

    // 從最大堆中取出堆頂元素, 即堆中所存儲的最大數據
    Item extractMax(){
        assert( count > 0 );
        Item ret = data[1];

        swap( data[1] , data[count] );
        count --;
        shiftDown(1);

        return ret;
    }

    // 獲取最大堆中的堆頂元素
    Item getMax(){
        assert( count > 0 );
        return data[1];
    }

};

5.堆排序

// heapSort1, 將所有的元素依次添加到堆中, 在將所有元素從堆中依次取出來, 即完成了排序
// 時間複雜度均爲O(nlogn)
template<typename T>
void heapSort1(T arr[], int n){

    MaxHeap<T> maxheap = MaxHeap<T>(n);
    for( int i = 0 ; i < n ; i ++ )
        maxheap.insert(arr[i]);

    for( int i = n-1 ; i >= 0 ; i-- )
        arr[i] = maxheap.extractMax();

}

優化1(heapify)

依據:因爲可以把葉子節點看作是已經滿足二叉堆

在這裏插入圖片描述

// heapSort2, 藉助我們的heapify過程創建堆
// 時間複雜度爲O(nlogn),比上述heapSort1性能更優, 因爲創建堆的性能更優
template<typename T>
void heapSort2(T arr[], int n){

    MaxHeap<T> maxheap = MaxHeap<T>(arr,n);
    for( int i = n-1 ; i >= 0 ; i-- )
        arr[i] = maxheap.extractMax();

}

優化2(原地堆排序)

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

// 原始的shiftDown過程
template<typename T>
void __shiftDown1(T arr[], int n, int k){

    while( 2*k+1 < n ){
        int j = 2*k+1;
        if( j+1 < n && arr[j+1] > arr[j] )
            j += 1;

        if( arr[k] >= arr[j] )break;

        swap( arr[k] , arr[j] );
        k = j;
    }
}


// 優化的shiftDown過程, 使用賦值的方式取代不斷的swap,
// 該優化思想和我們之前對插入排序進行優化的思路是一致的
template<typename T>
void __shiftDown(T arr[], int n, int k){

    T e = arr[k];
    while( 2*k+1 < n ){
        int j = 2*k+1;
        if( j+1 < n && arr[j+1] > arr[j] )
            j += 1;

        if( e >= arr[j] ) break;

        arr[k] = arr[j];
        k = j;
    }

    arr[k] = e;
}

// 不使用一個額外的最大堆, 直接在原數組上進行原地的堆排序
template<typename T>
void heapSort(T arr[], int n){

    // 注意,此時我們的堆是從0開始索引的
    // 從(最後一個元素的索引-1)/2開始
    // 最後一個元素的索引 = n-1
    for( int i = (n-1-1)/2 ; i >= 0 ; i -- )
        __shiftDown2(arr, n, i);

    for( int i = n-1; i > 0 ; i-- ){
        swap( arr[0] , arr[i] );
        __shiftDown(arr, i, 0);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章