1. 程式人生 > >【資料結構】十一種排序演算法C++實現

【資料結構】十一種排序演算法C++實現

   練習了十一種排序演算法的C++實現:以下依次為,冒泡、選擇、希爾、插入、二路歸併、快排、堆排序、計數排序、基數排序、桶排序,可建立sort.h和main.cpp將程式碼放入即可執行。如有錯誤,請指出更正,謝謝交流。

// sort.h
# include <iostream>
# include "chain.h"
using namespace std;

typedef struct ChainNode {
    int value;
    struct ChainNode *next;
    //ChainNode() {}
    ChainNode(int value, ChainNode * next) {
        this->value = value;
        this->next = next;
    }
} CNode, *Chain;

void swap(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}

void blob(int arr[], int len) {
    for (int i = len - 1; i > 0; i--)
        for (int j = 0; j < i; j++)
            if (arr[j] > arr[j + 1])
                swap(arr[j], arr[j + 1]);
}

void select(int arr[], int len) {
    for (int i = 0; i < len; i++) {
        int min = i;
        for (int j = i; j < len; j++)
            if (arr[j] < arr[min])
                min = j;
        swap(arr[i], arr[min]);
    }
}

void shell(int arr[], int len) {
    // https://zh.wikipedia.org/wiki/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F
    int i, j, step;
    for (step = len >> 1; step > 0; step >>= 1) // 初始步長為n/2,每次對步長取半直到步長達到1
        for (i = step; i < len; i++) { // 從第step個數開始,依次將後面的數與前面的step步長數進行比較
            int temp = arr[i];  // 將大於當前值的希爾序列中的值,不斷地進行後移,最終將當前值放到希爾序列的合適位置
            for (j = i - step; j >= 0 && arr[j] > temp; j -= step)  // 初始為當前位置的第前step個數,比較arr[j + step] 與 arr[j]
                arr[j + step] = arr[j];  // 希爾序列中前面的值,往後移(第一次會沖掉初始值temp)
            arr[j + step] = temp;
        }
}

void insert(int arr[], int len) {
    for (int i = 1; i < len; i++) {
        int temp = arr[i];
        int j;
        for (j = i - 1; j >= 0 && temp < arr[j]; j--)
            arr[j + 1] = arr[j];
        arr[j + 1] = temp;
    }
}

void merge(int arr[], int temp[], int start, int end) {
    if (start >= end)
        return;

    int mid = (start + end) / 2;
    int start1 = start, end1 = mid;
    int start2 = mid + 1, end2 = end;
    merge(arr, temp, start1, end1);
    merge(arr, temp, start2, end2);

    int k = start;  // 從start起始位置開始合併進temp中
    while (start1 <= end1 && start2 <= end2) { // 對兩個陣列同時處理,小的放到temp中
        temp[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
    }
    while (start1 <= end1)  // 歸併的兩個陣列不一定長,如果start1剩下,則合併進temp
        temp[k++] = arr[start1++];
    while (start2 <= end2)  // 如果第二個陣列剩下,將其合併進temp
        temp[k++] = arr[start2++];

    for (int k = start; k <= end; k++)  // 拷貝回原陣列
        arr[k] = temp[k];
}

void quick(int arr[], int start, int end) {
    if (start > end)
        return;

    int left = start, right = end;
    int mid = arr[left];  // 先在左邊第一個數挖個坑

    while (left < right) {
        while (left < right && arr[right] >= mid)  // 從右邊開始找小於中間值得第一個數
            right--;
        if (left < right) {
            arr[left] = arr[right]; // 將‘右邊的小於中間值的那個數’ 填入到 ‘左邊的坑(初始為第一個)’
            left++;  // 左邊坑被填好之後,處理下一個,往右挪一下
        }

        while (left < right && arr[left] <= mid)
            left++;
        if (left < right) {
            arr[right] = arr[left];  // 將‘左邊大於中間中的數’ 填入到 ‘右邊被挖出來的坑’
            right--; // 右邊坑被填好之後,處理下一個,往做挪一下
        }
    }

    arr[left] = mid; // //退出時,left等於right。將x填到這個坑中。這個坑也是中間數。

    quick(arr, start, left - 1);  // 中間的數不需要再排序
    quick(arr, left + 1, end);
}

void max_heapify(int arr[], int start, int end) {
    // 初始化dad和son
    int dad = start;
    int son = dad * 2 + 1;

    while (son <= end) { // 在不停地向下調整過程中,son不越過堆尾的前提下
        if (son + 1 <= end && arr[son + 1] > arr[son])  // 在不越過堆尾的前提下,找出最大的孩子節點的位置
            son++;
        if (arr[dad] > arr[son])  // 如果父節點值大於最大孩子節點值,則返回
            return;
        else {
            swap(arr[dad], arr[son]); // 交換父子節點值
            dad = son;  // 重新賦值dad和son,進一步的向下調整
            son = dad * 2 + 1;
        }
    }
}

void heap(int arr[], int len) {
    // https://zh.wikipedia.org/wiki/%E5%A0%86%E6%8E%92%E5%BA%8F
    // 建立初始大根堆,只有這裡考慮的是數量號len,其餘地方全部考慮索引號
    for (int i = len / 2 - 1; i >= 0; i--)  // 從最後一個父節點,依次向前開調整,len/2 - 1 可以確定最後一個父節點的索引位置。這裡使用節點數量len確定索引位置i,而不是索引數len-1(67確定索引2,(位置Wie3))
        max_heapify(arr, i, len - 1);

    // 逆序生成堆排序結果。依次取出堆頂元素,與最近一個尾部交換,重新調整大根堆,此時只需要調整堆頂一個元素即可
    for (int i = len - 1; i > 0; i--) {
        swap(arr[0], arr[i]); // 拿下堆頂元素,與堆尾元素交換
        max_heapify(arr, 0, i - 1); // 調整當前堆尾之前的所有元素為大根堆,只需要向下調整0號位置
    }
}

void count(int arr[], int len) {
    // https://zh.wikipedia.org/wiki/%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F
    // 找到陣列中的最大值最小值
    int min, max;
    min = max = arr[0];
    for (int i = 0; i < len; i++) {
        if (arr[i] > max)
            max = arr[i];
        else if (arr[i] < min)
            min = arr[i];
    }

    // 確定最大值,最小值之間的範圍差,並初始化相應大小的陣列
    int lenC = max - min + 1;
    int *arrC = new int[lenC];
    for (int k = 0; k < lenC; k++)
        arrC[k] = 0;

    // 遍歷原始陣列,將對應的新計數陣列上的值+1,每個值可以看作是一個桶
    for (int i = 0; i < len; i++) {
        int index = arr[i] - min;
        arrC[index] += 1;
    }

    // 遍歷計數陣列,輸出上面關聯的數值(桶裡的所有個數的數值)
    for (int k = 0, i = 0; k < lenC; k++) {
        if (arrC[k] != 0) {
            for (int j = 0; j < arrC[k]; j++)
                arr[i++] = k + min;  // 這裡的k+min就是真實值
        }
    }
}

int getMaxBit(int arr[], int len) {
    int max = arr[0];
    for (int i = 0; i < len; i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }        

    int maxBit = 0;
    while (max >= 10) {
        max /= 10;
        maxBit++;
    }
    return ++maxBit;
}

void radix(int arr[], int len) {
    // 求最大位數,個位為1,十位為2...
    int maxBit = getMaxBit(arr, len);
    cout << maxBit << endl;

    int *temp = new int[len];

    // 從小到達,遍歷每一位進行排序
    int radix = 1;
    for (int i = 0; i < maxBit; i++) {

        // 計數器清空
        int buckets[10] = { 0 };

        // 遍歷所有資料,將其放入到桶中
        for (int j = 0; j < len; j++)
        {
            int d = arr[j] / radix % 10;
            buckets[d] ++;  
        }

        // 修訂bucket陣列中值,索引位0的數值,代表0桶內有多少個數,索引位1上的值,代表1+0上的桶內一共有多少個數
        // 使得bucket中的陣列值,變成其索引所對應基數的偏移量,注意,預設均加了0號索引上的數量。
        for (int j = 1; j < 10; j++) {
            buckets[j] = buckets[j] + buckets[j - 1];
            cout << buckets[j] << endl;
        }

        // 將bucket中的數值儲存到temp陣列中
        for (int j = len - 1; j >= 0; j--) {
            int d = arr[j] / radix % 10;  // 找到桶索引,即基數
            temp[buckets[d] - 1] = arr[j]; // 桶索引中的數從1開始計數的,比如索引0上1個數,其值為1,但實際temp中索引從0開始。所以減1.
            buckets[d]--; // 儲存一個,則對應的基數上數值個數減一。這裡如果有6、7、8三個數,先賦值給index為7的位置,然後6,然後5.
        }

        // 將temp陣列賦值給原陣列,此時,完成低位上的排序有序
        for (int j = 0; j < len; j++)
            arr[j] = temp[j];

        radix *= 10;
    }

    delete []temp;    
}

void bucket(int arr[], int len) {
    // 初始化及設定固定空桶數
    int bucketNum = 10;  
    CNode **bucketTable = new CNode *[bucketNum];  // 分配動態二維(指標)陣列,bucketNum個CNode*陣列
    // vector<vector<CNode *> > bucketTable(bucketNum,vector<CNode *>(1)?));  // vector方法
    for (int i = 0; i < bucketNum; i++)  // 為指標陣列的每個元素,分配一個元素
        bucketTable[i] = new CNode(0, NULL);  // 初始化桶內元素計數,用第一個CNode?

    for (int i = 0; i < len; i++) {
        // 計算當前元素對映後的桶號
        int index = arr[i] / bucketNum;  
        CNode *p = bucketTable[index];

        // 將資料放到對應的空桶中, 並進行排序
        CNode *pNode = new CNode(arr[i], NULL);  // 新增陣列中的元素到桶中
        if (p->value == 0) {  // 該桶中還沒有資料  
            bucketTable[index]->next = pNode;
            (bucketTable[index]->value)++;  // 桶中數量加1
        }
        else {
            // 連結串列結構的插入排序
            while (p->next != NULL && p->next->value <= pNode->value)
                p = p->next;
            pNode->next = p->next;
            p->next = pNode;
            (bucketTable[index]->value)++;
        }
    }

    // 拼接不為空的桶中的資料,得到結果
    for (int i = 0; i < bucketNum; i++) {
        CNode *p = bucketTable[i]->next;  // bucketTable[i]這個CNode中包含了計數,其第一個next為第一個元素
        while (p) {
            cout << p->value << ",";
            p = p->next;
        }
    }

    for (int i = 0; i < bucketNum; i++)
    {
        delete[bucketNum]bucketTable[i];  // 先釋放指標陣列的每個元素指向的陣列
        bucketTable[i] = NULL;
    }
    delete[bucketNum]bucketTable;  // 再釋放該指標陣列
    bucketTable = NULL;

    // 建立二維動態陣列
    // https://blog.csdn.net/samuelcoulee/article/details/8674388
}

// main.cpp
# include <iostream>
# include "sort.h"
using namespace std;

void testSort() {
    int temp[8];
    int arr[] = { 7, 9, 1, 2, 6, 0, 5 , 8 };
    int len = sizeof(arr) / sizeof(int);
    for (int i = 0; i < len; i++)
        cout << arr[i] << ", ";

    // blob(arr, len);
    // select(arr, len);
    // insert(arr, len);
    // merge(arr, temp, 0, len-1);
    // quick(arr, 0, len - 1);
    // heap(arr, len);
    // shell(arr, len);
    // count(arr, len);
    // radix(arr, len);
    bucket(arr, len);

    for (int i = 0; i < len; i++)
        cout << arr[i] << ", ";
}


int main() {
    testSort();
}