1. 程式人生 > >C++_經典排序演算法

C++_經典排序演算法

1冒泡、2插入、3選擇、4快排、5堆排、6歸併、7希爾、8計數、9桶排、10基數。

//主要參考:以下兩個部落格總結
//https://blog.csdn.net/yushiyi6453/article/details/76407640
//https://blog.csdn.net/kellen_f/article/details/79029066
#include <iostream>
using namespace std;
template <class T>
void swap(T* num, int i, int j){
    T tmp;
    tmp = num[i];
    num[i] = num[j];
    num[j]= tmp;
}
//1.氣泡排序,時間複雜度O(n^2),空間複雜度O(1)
template <class T>
void BubbleSort(T *num, int size){
    if(size <= 1) return;
    for(int i = 0; i < size; i++){
        bool flag = false;
        for(int j = 0; j < size - i - 1; j++){
            if(num[j] > num[j+1]){
                swap(num, j, j+1);
                flag = true;
            }
        }
        if(!flag) return; //排好序則直接退出
    }
}
//2.插入排序,時間複雜度O(n^2),空間複雜度O(1)
template <class T>
void InsertSort(T *num, int size){
    if(size <= 1) return;
    for (int i = 1; i < size; i++){
        T tmp = num[i];
        int j;
        for(j = i - 1; j >= 0; j--){
            if(tmp < num[j]) num[j + 1] = num[j];
            else break;
        }
        num[j+1] = tmp;
    }
}
//3.簡單選擇排序,時間複雜度O(n^2),空間複雜度O(1)
template <class T>
void SelectSort(T *num, int size){
    if(size <= 1) return;
    for(int i = 0; i < size; i++){
        int min = num[i];
        int min_index = i;
        for (int j = i + 1; j < size; ++j) {
            if(num[j] < min){
                min = num[j];
                min_index = j;
                swap(num, min_index, i);
            }
        }
    }
}
//4.快排,時間複雜度O(nlogn),空間複雜度O(n)
//在陣列中隨機選一個數(預設陣列首個元素),陣列中小於等於此數的放在左邊,
//大於此數的放在右邊,再對陣列兩邊遞迴呼叫快速排序,重複這個過程。
template <class T>
void QuickSort(T *num, int s, int t){
    if(t <= 0) return;
    T tmp = num[s];
    int i = s;
    int j = t;
    if (s >= t) return;
    while(i < j){
        while(i < j && num[j] > tmp) j--;
        num[i] = num[j];
        while(i < j && num[i] < tmp) i++;
        num[j] = num[i];
    }
    num[i] = tmp;
    QuickSort(num, s, i - 1);
    QuickSort(num, i + 1, t);
}

//5.堆排序,時間複雜度O(nlogn),空間複雜度O(nlogn)
template <class T>
void HeapAdjust(T *num, int i, int size){
    // 調整以形成大頂堆
    int child = 2 * i + 1;//左孩子節點下標
    while(child < size){
        if(child + 1 < size && num[child] < num[child + 1]) child++;//右孩子更大
        if(num[child] > num[i]){
            swap(num, child, i);
            i = child;
            child = 2 * i + 1;
        }
        else break;//滿足根大於左右孩子,結束調整大頂堆
    }
}
template <class T>
void HeapSort(T* num, int size){
    if (size <=1 ) return;
    // 建立大頂堆
    for(int i = size / 2 - 1; i >= 0; --i){
        HeapAdjust(num, i, size);
    }
    // 取出堆頂,放到結尾,並重新調整堆
    for (int i = size - 1; i > 0; --i){
        swap(num, 0, i);
        HeapAdjust(num, 0, i); //第i個數據已經排序完成
    }
}
//6.歸併排序,時間複雜度O(nlogn), 空間複雜度O(nlogn)
template <class T>
void Merge(T *num, int begin, int mid, int end){
    if(begin >= end) return;
    T* tmp = new T[end - begin + 1];
    int id1 = begin, id2 = mid + 1;
    //合併兩段
    int i = 0;
    while(id1 <= mid && id2 <= end){
        if(num[id1] <= num[id2])
            tmp[i++] = num[id1++];
        else
            tmp[i++] = num[id2++];
    }
    // 繼續合併
    while(id1 <= mid) tmp[i++] = num[id1++];
    while(id2 <= end) tmp[i++] = num[id2++];
    // 將臨時資料寫回
    for(int j = 0; j < end - begin + 1; j++)
        num[begin + j] = tmp[j];
    delete []tmp;
}

template<class T>
void MergeSortway(T *num, int begin, int end){
    if(begin < end){
        int mid = (begin + end) / 2;
        MergeSortway(num, begin, mid);
        MergeSortway(num, mid + 1, end);
        Merge(num, begin, mid, end);
    }
}
template<class T>
void MergeSort(T *num, int size){
    if (size <= 1) return;
    MergeSortway(num, 0, size - 1);
}
//7.希爾排序,時間複雜度O(nlogn),空間複雜度O(1)
template <class T>
void ShellSort(T *num, int size){
    // 簡單插入排序的改進
    if(size <= 1) return;
    //增量div
    for(int div = size/2; div >= 1; div = div / 2){
        //分成div組
        for(int i = 0; i < div; ++i){
            // 對每組進行插入排序
            for(int j = i; j < size - div; j += div){
                for(int k = j; k < size; k += div){
                    if(num[j] > num[k]) swap(num, j, k);
                }
            }
        }
    }
}
//8.計數排序,時間複雜度O(n+k),k是整數的範圍,空間複雜度O(n+k)
void CountSort(int num[], int size){
  /*- 找出待排序的陣列中最大和最小的元素
    - 統計陣列中每個值為i的元素出現的次數,存入陣列C的第i項
    - 對所有的計數累加(從C中的第一個元素開始,每一項和前一項相加)
    - 反向填充目標陣列:將每個元素i放在新陣列的第C(i)項,每放一個元素就將C(i)減去1
    */
    if(size <= 1) return;
    int min = num[0];
    int max = num[0];
    for(int i = 1; i < size; ++i){
        if (min > num[i]) min = num[i];
        if (max < num[i]) max = num[i];
    }
    int tmp_size = max - min + 1;
    int count_num[tmp_size];
    for(int i = 0; i < size; ++i){
        count_num[num[i] - min]++;
    }
    int index = 0;
    for(int i = 0; i < tmp_size; ++i){
        while(count_num[i]-- > 0){
            num[index++] = i + min;
        }
    }

}
//9. 桶排序,時間複雜度O(n),空間複雜度O(n)
//計數排序是簡單而特殊的桶排序
/*
 桶排序將[0,1)區間劃分為n個相同的大小的子區間,這些子區間被稱為桶。
 然後將n個輸入元素分別放入各自的桶中。因為輸入時均勻獨立的,所以一般不會有很多數同時落在一個桶中的情況。
 這樣,我們想對各個桶中的資料進行排序,然後遍歷每個桶,按照次序把各個桶中的元素列出來即可。
 */

//10.基數排序,時間複雜度O(n*digit),空間複雜度O(n),digit數字的位數
/*
 * 數排序又稱為“桶子法”,從低位開始將待排序的數按照這一位的值放到相應的編號為0~9的桶中。
 * 等到低位排完得到一個子序列,再將這個序列按照次低位的大小進入相應的桶中,一直排到最高位為止,
 * 陣列排序完成。
 */
int main(){
    int num[10] = {2, 5, 1, 3, 0, 7, 9, 8, 4, 6};
    CountSort(num, 10);
    for(int i = 0; i < 10; i++)
        cout<<num[i]<<" ";
    return 0;
}