1. 程式人生 > >排序演算法程式碼詳解

排序演算法程式碼詳解

#include <iostream>
using namespace std;

void print(int a[], int len)
{
        for(int i = 0; i < len; i++)
        cout << a[i] << " ";
        cout << endl;
}

void swap(int &a, int &b)
{
    if( a > b)
    {
        int t = a;
        a = b;
        b = t;
    }
}

void bubble_sort(int a[], int len)
{
    //氣泡排序
    int exchange = 0;        //設定標記
    for(int i = 0; i < len - 1; i++)    //總共有 n-1 次排序
    {
        exchange = 0;    //每次把標記置 0
        for(int j = len - 2; j >= i; j--)  //從後往前比較
        {
            if(a[j] > a[j+1])    //比較大小,交換資料
            {
                int tmp = a[j];  
                a[j] = a[j+1];
                a[j+1] = tmp;
                exchange = 1;     //發生資料交換,標記置1
            }
        }
        if(exchange != 1)    //標記不為1,跳過這次迴圈
                break;
    }
}

void select_sort(int a[], int len)
{
    //簡單選擇排序,每次找到最小的那個數和第一個數交換位置
    int i, j, tmp, min;
    for(i = 0; i < len -1 ;i++)  // n-1 次 迴圈
    {
        tmp = a[i];    //記錄每次第一個元素的值和下標
        min = i;
        for(j = i+1; j < len; j++)
        {
            if(a[j] < tmp)   //如果當前位置的值小於第一個元素的值
            {
                tmp = a[j];   //把當前元素的值和下標記錄下來
                min = j;
            }  
        }
        if(min != i)   //如果flag標記發生變化,表示有元素的值比第一個數小
        {
            a[min] = a[i];  //交換兩個數的值
            a[i] = tmp;    
        }
    }
}

void insert_sort(int a[], int len)
{
    //直接插入排序:把第一個數當成一個有序陣列,後面的數一個一個有序的插入的陣列中
    int i, j, tmp;
    for(i = 1; i < len; i++)
    {    
        tmp = a[i];   //tmp用來記錄要插入的數的值

        //滿足條件,往後挪。當 tmp > a[j] 小,tmp 放在 a[j] 後面 
        for(j = i-1; j >= 0 && tmp < a[j]; j--) 
            a[j+1] = a[j];  
        a[j+1] = tmp;       
    }
}

void shell_sort(int a[], int len)
{
    int d = len;
    while(d > 1)
    {
        d = (d + 1)/2;  //設定增量
        for(int i = 0; i < len -d; i++) 
        {
            if(a[i] > a[i+d])  //交換相隔距離 d 的兩個數的值
            {
                int tmp = a[i];
                a[i] = a[i+d];
                a[i+d] = tmp;
            }
        }
    }
}

void heap_adjust(int a[], int len, int index)
{
    int left = 2*index + 1;    //父節點左孩子下標
    int right = 2*index + 2;   //父節點有孩子下標
    int maxindex = index;      //令最大值的下標為當前父節點下標
    if(left < len && a[left] > a[maxindex])      //在陣列範圍內且左孩子的值大於父節點的值
        maxindex = left;                         //最大值下標變為左孩子下標
    if(right < len && a[right] > a[maxindex])    //在陣列範圍內且右孩子的值大於父節點的值
        maxindex = right;                        //最大值下標變為右孩子下標
    if(maxindex != index)                        //當最大值下標更新
    {
        swap(a[maxindex], a[index]);             //交換最大值下標對應的值和父節點的值
        heap_adjust(a, len, maxindex);           //遞迴調整其他不滿足堆性質的部分
    }
}

void heap_sort(int a[], int len)
{
    int i;            //最後一個非葉子節點的下標為 len/2 -1 
    for(i = len/2 -1; i >= 0; i--)    //對每一個父節點進行調整,使堆變成大頂堆(從最後一個父節點開始) 非葉子結點即父節點
    {
        heap_adjust(a, len, i);       // i 為當前父節點下標
    }
    for(i = len -1; i > 0; i--)       //從後往前,儲存最大的數
    {
        swap(a[0], a[i]);             //把經過調整的大頂堆的最大值和最後一個元素互換位置,放到陣列末尾
        heap_adjust(a, i, 0);          //將剩下的部分繼續進行堆排序
    }
}

void merge(int a[], int begin, int middle, int end)
{
    int i, j, k, leftlen, rightlen;
    leftlen = middle - begin + 1;    //左區間元素個數
    rightlen = end - middle;         //右區間元素個數

    int *L = new int(leftlen);      //申請兩個陣列用來分別存放 左區間 和  右區間 的元素
    int *R = new int(rightlen);

    for(i = 0, k = begin; i < leftlen; i++, k++)   //對 陣列 L 進行賦值
    {
        L[i] = a[k];
    }

    for(i = 0, k = middle + 1; i < rightlen; i++, k++)   //對 陣列 R 進行賦值
    { 
        R[i] = a[k];
    }

    for(k = begin, i = 0, j = 0; i < leftlen && j < rightlen; k++)   //對原陣列進行重新排序
    {
        if(L[i] < R[j])       //左區間當前下標的值小於右區間的值
        {
            a[k] = L[i];      //陣列當前下標元素的值為左區間的值
            i++;              //左區間下標加1
        }
        else                  //反之
        {
            a[k] = R[j];      //陣列當前下標元素的值為右區間的值
            j++;              //右區間下標加1
        }
    }

    if(i < leftlen)          //這兩個 if 語句 是將 左區間 和 右區間 歸併剩下的資料 放到陣列的最後面
    {
        for(j = i; j < leftlen; j++, k++)  // j = i  屬於習慣問題    可以寫成  for (; i < n1; i++, k++)
        {
            a[k] = L[j]; 
        }
    }

    if(j < rightlen)
    {
        for(i = j; i < rightlen; i++, k++)  // i = j  屬於習慣問題    可以寫成  for (; j < n2; j++, k++)
        {
            a[k] = R[i];
        }
    }

    delete [] L;
    delete [] R;
}

void merge_sort(int a[], int begin, int end)
{
    if(begin < end)
    {
        int middle  = (begin + end) / 2;   //定義分界點,將陣列分為兩部分
        merge_sort(a, begin, middle);      //對左區間 [begin, middle] 遞迴做歸併排序
        merge_sort(a, middle + 1, end);    //對右區間 [middle + 1, end] 遞迴做歸併排序
        merge(a, begin, middle, end);      //組合,將兩個有序的區塊合併成一個有序的區塊
    }
}

void quick_sort(int a[], int begin, int end)
{
    if(begin < end)
    {
        int pivot = a[begin];      //將陣列第一個數作為比較關鍵字,儲存下來
        int i = begin;             //從前往後
        int j = end;               //從後往前
        while(i < j)
        {
            while(i < j && a[j] >= pivot)   // i < j 且 a[j] 大於等於 關鍵字 時
                j--;                        // j--, 下標向前挪一位   
            if(i < j && a[j] < pivot)       // i < j 且 a[j] 小於 關鍵字 時
                a[i++] = a[j];              // 令 a[i] = a[j]  a++ 下標向後挪一位
            while(i < j && a[i] <= pivot)   // i < j 且 a[i] 小於等於 關鍵字 時
                i++;                        // i++, 下標向後挪一位
            if(i < j && a[i] > pivot)       // i < j 且 a[i] 大於 關鍵字 時
                a[j--] = a[i];              // 令 a[j] = a[i]  j-- 下標向前挪一位
        }
        a[i] = pivot;
        quick_sort(a, begin, i - 1);
        quick_sort(a, i+1, end);
    }
}

int main()
{
    int a[] = {7,1,3,2,5,6,4,9,8};
    int len  = sizeof(a)/sizeof(a[0]);

    cout << "排序前" << endl;
    print(a, len);

    // 氣泡排序:兩輛比較關鍵字,反序則交換
    // cout << "氣泡排序" << endl;
    // bubble_sort(a, len);
    // print(a, len);

    // 簡單選擇排序:通過(n - i)次關鍵字間的比較, 從(n - i - 1)個記錄中選擇關鍵字最小的記錄,並和第 i 個記錄交換位置
    // cout << "選擇排序" << endl;
    // select_sort(a, len);
    // print(a, len);

    // 直接插入排序:將第一個數視為有序陣列,把後面每一個數都有序插入到陣列中,使整個序列有序
    // cout << "直接插入排序" << endl;
    // insert_sort(a, len);
    // print(a, len);

    // 希爾排序:把相距某一個增量 d( d = len; d = (d+1)/ 2;) 的記錄組成子序列,然後在子序列內進行直接插入排序,再對全體記錄進行一次直接插入排序
    // cout << "Shell排序" << endl;
    // shell_sort(a, len);
    // print(a, len);
    
    // 堆排序:將待排序的序列構造成一個大頂堆(完全二叉樹,根節點最大),將根節點和堆末尾元素交換,然後將剩餘的元素重新構造堆,再選出最大值,反覆執行
    // cout << "堆排序" << endl;
    // heap_sort(a, len);
    // print(a, len);

    // 將序列拆分成多個單個數組,再兩兩歸併,得到有序的多個子序列,反覆執行直至只有一個有序的序列為止
    // cout << "歸併排序" << endl;
    // merge_sort(a, 0, len -1);
    // print(a, len);

    //快速排序:在一堆待排序記錄中選擇關鍵字,將序列分為兩組,一組比關鍵字都小,另一組比關鍵字都大,遞迴執行,可得到一個有序序列
    cout << "快速排序" << endl;
    quick_sort(a, 0, len - 1);
    print(a, len);

    return 0;
}