內部排序法小結

1.冒泡排序(Bubble Sort)

冒泡排序方法是最簡單的排序方法。這種方法的基本思想是,將待排序的元素看作是豎着排列的“氣泡”,較小的元素比較輕,從而要往上浮。在冒泡排序算法中我們要對這個“氣泡”序列處理若干遍。所謂一遍處理,就是自底向上檢查一遍這個序列,並時刻注意兩個相鄰的元素的順序是否正確。如果發現兩個相鄰元素的順序不對,即“輕”的元素在下面,就交換它們的位置。顯然,處理一遍之後,“最輕”的元素就浮到了最高位置;處理二遍之後,“次輕”的元素就浮到了次高位置。在作第二遍處理時,由於最高位置上的元素已是“最輕”元素,所以不必檢查。一般地,第i遍處理時,不必檢查第i高位置以上的元素,因爲經過前面i-1遍的處理,它們已正確地排好序。
冒泡排序是穩定的。算法時間複雜度是O(n^2)。

//冒泡排序(相鄰比較法)
void BubbleSort(int a[],int n)
{
    int i,j;
    int temp;
    for(i = 0; i < n-1; i++)
        for(j = 0; j < n-i-1; j++)
            if(a[j] > a[j+1])
            {
                temp = a[j];
                a[j] = a[j+1];
                a[j+1] = temp;
            }
}
//改進的冒泡排序
void ImprovedBubbleSort(int a[], int n)
{
    int i,j;
    int temp;
    bool change = false;
    for (i = 0; i < n-1; i++)
    {
        change = false;
        for (int j = 0; j < n-i-1; j++)
        if (a[j] > a[j+1])
        {
            temp = a[j];
            a[j] = a[j+1];
            a[j+1] = temp;
            change = true;
        }
        if ( !change ) break;
    }
}

2.選擇排序(Selection Sort)

選擇排序的基本思想是對待排序的記錄序列進行n-1遍的處理,第i遍處理是將L[i..n]中最小者與L[i]交換位置。這樣,經過i遍處理之後,前i個記錄的位置已經是正確的了。
選擇排序是不穩定的。算法複雜度是O(n^2 )。

//選擇排序
void SelectSort(int a[],int n)
{
    int temp;
    int i,j;
    for (i = 0; i < n; i++)
    {
        int k = i;
        for (j = i+1; j < n; j++)
            if (a[j] < a[k]) k = j;
        if(k != i)
        {
           temp = a[i];
           a[i] = a[k];
           a[k] = temp;
        }
    }
}

3.插入排序(Insertion Sort)

插入排序的基本思想是,經過i-1遍處理後,L[1..i-1]己排好序。第i遍處理僅將L[i]插入L[1..i-1]的適當位置,使得L[1..i]又是排好序的序列。要達到這個目的,我們可以用順序比較的方法。首先比較L[i]和L[i-1],如果L[i-1]<=L[i],則L[1..i]已排好序,第i遍處理就結束了;否則交換L[i]與L[i-1]的位置,繼續比較L[i-1]和L[i-2],直到找到某一個位置j(1<=j<=i-1),使得L[j] <=L[j+1]時爲止。
直接插入排序是穩定的。算法時間複雜度是O(n^2)。

//插入排序
void InsertSort(int a[], int n)
{
    int i, j;
    int temp;
    for (i = 1; i < n; i++)
    {
        temp = a[i];
        j = i -1;
        while(j >= 0 && a[j] > temp;)
        {
            a[j+1] = a[j];
            --j;
        }
        a[j+1] = temp;
    }
}

//遞歸的插入排序
void RecursiveInsertSort(int a[], int n)
{
    int i, j, key;
    if(n > 1)
    {
        RecursiveInsertSort(a,n-1);
    }
    key = a[n-1];
    i = n-2;
    while(i >= 0 && a[i] > key)
    {
        a[i+1] = a[i];
        i--;
    }
    a[i+1] = key;
}

//折半插入排序
void BinInsertSort(int a[], int n)
{
    int i,j;
    for (i = 1; i < n; i++)
    {
        // 在a[0..i-1]中折半查找插入位置使a[high]<=a[i]<a[high+1..i-1]
        int low = 0, high = i-1, m = 0;
        while (low <= high)
        {
            m = m + (high-low)>>1;
            if (a[i] < a[m]) high = m-1;
            else low = m+1;
        }
        // 向後移動元素a[high+1..i-1],在a[high+1]處插入a[i]
        int x = a[i];
        for (j = i-1; j > high; j--)
            a[j+1] = a[j];
        a[high+1] = x; 	// 完成插入
    }
}

4.希爾排序(Shell Sort)

先將待排序列分割成若干個子序列,分別進行直接插入排序,基本有序後再對整個序列進行直接插入排序。
希爾排序是不穩定的。時間複雜度大約爲O(n^3/2)。

//希爾排序:shell排序的核心仍然使用插入排序
void ShellSort(int a[], int n)
{
    int dk = n/2;
    int i,j;
    while (dk >= 1)
    {
        // 一趟希爾排序,對dk個序列分別進行插入排序
        for (i = dk; i < n; ++i)
        {
            int x = a[i];
            for (j = i-dk; j >= 0 && a[j] > x; j -= dk )
                a[j+dk] = a[j];
            a[j+dk] = x;
        }
        dk = dk/2;  // 縮小增量
    }
}

5.堆排序(Heap Sort)

堆排序是一種樹形選擇排序,在排序過程中,將A[n]看成是完全二叉樹的順序存儲結構,利用完全二叉樹中雙親結點和孩子結點之間的內在關係來選擇最小的元素。對N個元素從小到大排序建立大根堆,然後交換堆頂與最後一個元素,將剩下的N-1個元素調整爲大根堆,執行N-1此這樣的操作。
堆排序是不穩定的。算法時間複雜度O(nlogn)。

//調整大根堆
void HeapAdjust(int data[],int nStart, int nLen)
{
    int nMaxChild = 0;
    int Temp;

    while ((2*nStart+1) < nLen)
    {
        nMaxChild = 2*nStart+1;
        if ( (2*nStart+2) < nLen)
        {
            //比較左子樹和右子樹,記錄最大值的Index
            if (data[2*nStart+1] < data[2*nStart+2])
            {
                nMaxChild = 2*nStart+2;
            }
        }
        //change data
        if (data[nStart] < data[nMaxChild])
        {
            //交換nStart與nMaxChild的數據
            Temp = data[nStart];
            data[nStart] = data[nMaxChild];
            data[nMaxChild] = Temp;

            //堆被破壞,需要重新調整
            nStart = nMaxChild;
        }
        else
        {
            //比較左右孩子均小則堆未破壞,不再需要調整
            break;
        }
    }
}

//堆排序 從小到大排序建立大頂堆
void HeapSort(int data[],int nLen)
{
    int i;
    int nTemp;
    //建立堆
    for (i = nLen/2-1; i >= 0; i--)
    {
        HeapAdjust(data, i, nLen);
    }
    for (i = nLen-1; i > 0; i--)
    {
        //交換堆頂元素和最後一個元素
        nTemp = data[0];
        data[0] = data[i];
        data[i] = nTemp;

        //將data[0...i]重寫建成大根堆
        HeapAdjust(data, 0, i);
    }
}

6.快速排序(Quick Sort)

快速排序是對冒泡排序的一種本質改進。它的基本思想是通過一趟掃描後,使得排序序列的長度能大幅度地減少。在冒泡排序中,一次掃描只能確保最大數值的數移到正確位置,而待排序序列的長度可能只減少1。快速排序通過一趟掃描,就能確保某個數(以它爲基準點吧)的左邊各數都比它小,右邊各數都比它大。然後又用同樣的方法處理它左右兩邊的數,直到基準點的左右只有一個元素爲止。

快速排序是不穩定的。最理想情況算法時間複雜度O(nlog2n),最壞O(n^2)。

//快速排序
void QuickSort(int a[], int low, int high)
{
    if (low < high)
    {
        // 劃分
        int pivot = a[low];
        int i = low; int j = high;
        while (i < j)
        {
            while (i<j && a[j] >= pivot)  j--;
            a[i] = a[j];
            while (i<j && a[i] <= pivot)  i++;
            a[j] = a[i];
        }
        a[i] = pivot;
        // 對子序列快排
        QuickSort(a, low, i-1);
        QuickSort(a, i+1, high);
    }
}

//快速排序法的劃分操作
int Partition(int vec[],int low,int high)
{
     //任選元素作爲軸,這裏選首元素
     int pivot = vec[low];
     while(low < high)
     {
         while(low < high && vec[high] >= pivot)
             high--;
         vec[low] = vec[high];
         while(low < high && vec[low] <= pivot)
             low++;
         vec[high] = vec[low];
     }
     //此時low==high
     vec[low] = pivot;
     return low;
}

//in-place partition algorithm
//http://en.wikipedia.org/wiki/Quicksort
int in_place_partition(int* array, int left, int right)
{
    int index = left;
    int pivot = array[index];
    swap(array[index], array[right]);
    for (int i=left; i<right; i++)
    {
        if (array[i] < pivot)    //升序
        {
            swap(array[index], array[i]);
            ++index;
        }
    }
    swap(array[right], array[index]);
    return index;
}

void Qsort(int* array, int left, int right)
{
    if (left >= right)
        return;
    int index = Partition(array, left, right);
    Qsort(array, left, index - 1);
    Qsort(array, index + 1, right);
}

//非遞歸快速排序
void QuickSort2(int vec[],int low,int high)
{
    stack<int> st;
    if(low < high)
    {
        int mid = Partition(vec,low,high);
        if(low < mid-1)
        {
             st.push(low);
             st.push(mid-1);
         }
         if(mid+1 < high)
         {
             st.push(mid+1);
             st.push(high);
         }
         //用棧保存每個待排序子串的首尾元素下標,下一次循環時取出這個範圍,對這段子序列進行partition操作
         while(!st.empty())
         {
             int q = st.top();
             st.pop();
             int p=st.top();
             st.pop();
             mid = Partition(vec,p,q);
             if(p < mid-1)
             {
                 st.push(p);
                 st.push(mid-1);
             }
             if(mid+1<q)
             {
                 st.push(mid+1);
                 st.push(q);
             }
         }
     }
}

8.歸併排序(Merge Sort)

基本思想是合併兩個有序表,設有兩個有序(升序)序列存儲在同一數組中相鄰的位置上,不妨設爲A[l..m],A[m+1..h],將它們歸併爲一個有序數列,並存儲在A[l..h]。
歸併排序的時間複雜度無論是在最好情況下還是在最壞情況下均是O(nlog2n)。

歸併排序在最壞的情況下都是O(NlogN)的時間複雜度,缺點是Merge的時候要有O(N)的額外的空間,如何改進?使用In-place Merge Sort:http://blog.ibread.net/345/in-place-merge-sort/

//將有序序列a[low..mid]和a[mid+1..high]歸併到a[low..high]。
void Merge(int a[], int low, int mid, int high)
{
    // 歸併到b[]
    int i = low;
    int j = mid+1;
    int k = 0;
    int *b = new int[high-low+1];
    while (i <= mid && j <= high)
    {
        if (a[i] <= a[j]) { b[k++] = a[i++]; }
        else  { b[k++] = a[j++]; }
    }
    // 歸併剩餘元素
    while (i <= mid)  b[k++] = a[i++];
    while (j <= high)  b[k++] = a[j++];
    // 從b[]複製回a[]
    for(i = 0; i <= high-low; ++i)
        a[low+i] = b[i];
    delete []b;
}

//歸併排序
//http://en.wikipedia.org/wiki/Mergesort
void MergeSort(int a[], int low, int high)
{
    if(low >= high)  return;
    else
    {
        int mid = (low+high)/2;
        MergeSort(a,low,mid);
        MergeSort(a,mid+1,high);
        Merge(a,low,mid,high);
    }
}

//自底向上的歸併排序
void MergeSort2(int a[], int n)
{
    int s,i,t = 1;
    while(t < n)
    {
        s = t;  t = s*2;
        for(i=0; i+t<=n; i+=t)
            Merge(a,i,i+s-1,i+t-1);
        if(i+s < n)
            Merge(a,i,i+s-1,n-1);
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章