1. 程式人生 > >資料結構與演算法C++之堆排序

資料結構與演算法C++之堆排序

首先需要介紹一下一個新的資料結構:堆
堆使用了優先佇列
普通佇列:先進先出,後進後出
優先佇列:出隊順序與入隊順序無關,與優先順序有關,一般取出優先順序最高的元素,堆入隊出隊的演算法複雜度都為O(nlogn)
最常使用的是二叉堆(Binary Heap)
在這裡插入圖片描述
如上圖所示,62稱為41和30的父節點,41稱為左節點,30稱為右節點,以此類推,41又是28和16的父節點,等等,二叉堆有如下性質:

  • 父節點大於左右子節點
  • 堆總是一棵完全二叉樹,即最後一層上面的所有層都是完整的,即因為上圖有四層,那麼30一定同時存在左右節點,否則就不滿足堆的性質,倒數第二層的父節點可以左右節點都沒有,如22,也可以只有一個節點,但這個節點必須是左節點,如16
    用陣列來儲存二叉堆,如下圖所示
    在這裡插入圖片描述

習慣於將最上面的父節點62編號為1,然後從上到下,從左到右依次編號,因為編號從1開始,所以用來儲存堆的陣列需要10+1個儲存空間

  • 定義某個節點 i i 的父節點為 p a r
    e n t ( i ) = i / 2
    parent(i)=i/2
  • 定義該節點 i i 的左節點為 l e f t c h i l d ( i ) = 2 i left child (i) = 2*i
  • 定義該節點 i i 的又節點為 r i g h t c h i l d ( i ) = 2 i + 1 right child (i) = 2*i + 1

如節點4,它的值是28,他的父節點為 4 / 2 = 2 4/2 = 2 ,值是41
它的左節點為 2 4 = 8 2*4 = 8 ,值是19
它的右節點為 2 4 + 1 = 9 2*4 + 1 = 9 , 值是17

Shift Up 堆中插入元素
在這裡插入圖片描述
如上圖所示,插入元素值為52

(1)首先將52插入堆的最後一個位置,編號為11
在這裡插入圖片描述
(2)將52與其父節點16作比較,比16大,那麼交換16和52的位置
在這裡插入圖片描述
(3)將52再與其父節點41作比較,比41大,那麼交換41和52的位置
在這裡插入圖片描述
(4)將52再與新的父節點62作比較,比62小,那麼插入操作結束
在這裡插入圖片描述

Shift Down 堆中取出最大元素

(1)首先將最上面的父節點62取出
在這裡插入圖片描述
(2)將最後一個節點16填補到最上面的位置,同時將堆的元素總數count減1
在這裡插入圖片描述
(3)然後將父節點16與其他兩個子節點52和30作比較,16比52小,那麼交換16與52的位置
在這裡插入圖片描述
(4)繼續比較16與它的兩個新的子節點28和41的大小,比41小,那麼交換16和41的位置
在這裡插入圖片描述
(4)繼續比較16與其子節點15的大小,16比15大,那麼不用交換位置,Shift Down結束
在這裡插入圖片描述
下面就先使用ShiftUp和ShiftDown進行插入元素和取出元素

#include <iostream>
#include <algorithm>
#include <string>
#include <ctime>
#include <cmath>
#include <cassert>
#include <typeinfo>

#ifndef _SORTINGHELP_H_
#define _SORTINGHELP_H_
#include "SortingHelp.h"
#endif // _SORTINGHELP_H_

#include "MergeSorting.h"
#include "quickSorting.h"

using namespace std;

template<typename Item>
class MaxHeap{
private:
    Item *data;
    int count;
    int capacity;

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

    void ShiftDown(int k){
        while (k <= count/2){
            int j = 2*k; //此輪迴圈中,data[k]和data[j]交換位置
            if (data[j] < data[j+1] && j + 1 <= count)
                j += 1;
            if (data[k] >= data[j])
                break;

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

public:
    MaxHeap(int capacity){
        data = new Item[capacity + 1];
        count = 0;
        this->capacity = capacity;
    }

    ~MaxHeap(){
        delete[] data;
    }

    int size(){
        return count;
    }

    bool isEmpty(){
        return count == 0;
    }

    void insert(Item item){

        assert( count + 1 <= capacity );
        data[count+1] = item;
        count ++;
        ShiftUp(count);
    }

    Item extractMax(){
        assert( count > 0);

        Item ret = data[1];

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

        return ret;
    }

public:
    void testPrint(){ //列印堆函式,不用掌握

        if( size() >= 100 ){
            cout<<"Fancy print can only work for less than 100 int";
            return;
        }

        if( typeid(Item) != typeid(int) ){
            cout <<"Fancy print can only work for int item";
            return;
        }

        cout<<"The Heap size is: "<<size()<<endl;
        cout<<"data in heap: ";
        for( int i = 1 ; i <= size() ; i ++ )
            cout<<data[i]<<" ";
        cout<<endl;
        cout<<endl;

        int n = size();
        int max_level = 0;
        int number_per_level = 1;
        while( n > 0 ) {
            max_level += 1;
            n -= number_per_level;
            number_per_level *= 2;
        }

        int max_level_number = int(pow(2, max_level-1));
        int cur_tree_max_level_number = max_level_number;
        int index = 1;
        for( int level = 0 ; level < max_level ; level ++ ){
            string line1 = string(max_level_number*3-1, ' ');

            int cur_level_number = min(count-int(pow(2,level))+1,int(pow(2,level)));
            bool isLeft = true;
            for( int index_cur_level = 0 ; index_cur_level < cur_level_number ; index ++ , index_cur_level ++ ){
                putNumberInLine( data[index] , line1 , index_cur_level , cur_tree_max_level_number*3-1 , isLeft );
                isLeft = !isLeft;
            }
            cout<<line1<<endl;

            if( level == max_level - 1 )
                break;

            string line2 = string(max_level_number*3-1, ' ');
            for( int index_cur_level = 0 ; index_cur_level < cur_level_number ; index_cur_level ++ )
                putBranchInLine( line2 , index_cur_level , cur_tree_max_level_number*3-1 );
            cout<<line2<<endl;

            cur_tree_max_level_number /= 2;
        }
    }

private:
    void putNumberInLine( int num, string &line, int index_cur_level, int cur_tree_width, bool isLeft){

        int sub_tree_width = (cur_tree_width - 1) / 2;
        int offset = index_cur_level * (cur_tree_width+1) + sub_tree_width;
        assert(offset + 1 < line.size());
        if( num >= 10 ) {
            line[offset + 0] = '0' + num / 10;
            line[offset + 1] = '0' + num % 10;
        }
        else{
            if( isLeft)
                line[offset + 0] = '0' + num;
            else
                line[offset + 1] = '0' + num;
        }
    }

    void putBranchInLine( string &line, int index_cur_level, int cur_tree_width){

        int sub_tree_width = (cur_tree_width - 1) / 2;
        int sub_sub_tree_width = (sub_tree_width - 1) / 2;
        int offset_left = index_cur_level * (cur_tree_width+1) + sub_sub_tree_width;
        assert( offset_left + 1 < line.size() );
        int offset_right = index_cur_level * (cur_tree_width+1) + sub_tree_width + 1 + sub_sub_tree_width;
        assert( offset_right < line.size() );

        line[offset_left + 1] = '/';
        line[offset_right + 0] = '\\';
    }
};



int main(){
    MaxHeap<int> maxheap = MaxHeap<int>(100);
    srand(time(NULL));
    for (int i = 0; i < 10; i++){
        maxheap.insert(rand()%100);
    }
    maxheap.testPrint();
    return 0;
}

輸出為
在這裡插入圖片描述
取出元素測試程式為

int main(){
    MaxHeap<int> maxheap = MaxHeap<int>(100);
    srand(time(NULL));
    for (int i = 0; i < 10; i++){
        maxheap.insert(rand()%100);
    }
    //maxheap.testPrint();
    while(!maxheap.isEmpty()){
        cout<<maxheap.extractMax()<<" ";
    }
    cout<<endl;
    return 0;
}

輸出為
在這裡插入圖片描述
下面使用堆實現排序
其實就是先將陣列中元素挨個放入堆中,然後挨個將堆中元素取出來逆序再放入陣列中就可以
首先將堆的實現放入 Heap.h 中

//Heap.h

#include <iostream>
#include <algorithm>
#include <string>
#include <ctime>
#include <cmath>
#include <cassert>
#include <typeinfo>

using namespace std;

template<typename Item>
class MaxHeap{
private:
    Item *data;
    int count;
    int capacity;

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

    void ShiftDown(int k){
        while (k <= count/2){
            int j = 2*k; //此輪迴圈中,data[k]和data[j]交換位置
            if (data[j] < data[j+1] && j + 1 <= count)
                j += 1;
            if (data[k] >= data[j])
                break;

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

public:
    MaxHeap(int capacity){
        data = new Item[capacity + 1];
        count = 0;
        this->capacity = capacity;
    }

    ~MaxHeap(){
        delete[] data;
    }

    int size(){
        return count;
    }

    bool isEmpty(){
        return count == 0;
    }

    void insert(Item item){

        assert( count + 1 <= capacity );
        data[count+1] = item;
        count ++;
        ShiftUp(count);
    }

    Item extractMax(){
        assert( count > 0);

        Item ret = data[1];

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

        return ret;
    }

public:
    void testPrint(){

        if( size() >= 100 ){
            cout<<"Fancy print can only work for less than 100 int";
            return;
        }