內部排序法總結


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;
    }
}
//雙向冒泡排序(雞尾酒排序)
void CocktailSort(int a[], int n)
{
    int top = n - 1;
    int bottom = 0;
    bool flag = true;
    int i, j;
    while(flag)
    {
        flag = false;
        //從小到大,升序
         for(i = bottom; i < top; i++)
        {
            if(a[i] > a[i+1])
            {
                swap(a[i], a[i+1]);
                flag = true;
            }
        }
        top--;

        //從大到小,降序
        for(j = top; j > bottom; j--)
        {
            if(a[j] < a[j-1])
            {
                swap(a[j], a[j-1]);
                flag = true;
            }
        }
        bottom++;
    }
}

An example of bubble sort. Starting from the beginning of the list, compare every adjacent pair, swap their position if they are not in the right order (the latter one is smaller than the former one). After each iteration, one less element (the last one) is needed to be compared until there are no more elements left to be compared.

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;
        }
    }
}

Selection sort animation. Red is current min. Yellow is sorted list. Blue is current item.

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)/2;
            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;     // 完成插入
    }
}

A graphical example of insertion sort.

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 pivot_index = (right-left)/2;
    int pivot = array[pivot_index];
    swap(array[pivot_index], array[right]);
    int index = left;
    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);
}

非遞歸的快速排序:用棧保存每個待排序子串的首尾元素下標,下一次循環時取出這個范圍,對這段子序列進行partition操作,直到所有的子串都排好序,棧也為空了。

//非遞歸快速排序
void QuickSort2(int vec[],int low,int high)
{
    stack<int> st;
    if(low >= high) return;

    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);
        }
    }
}

7.歸並排序(Merge Sort)

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

歸並排序是穩定的排序算法。

//將有序序列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);
    }
}

歸並排序在最壞的情況下都是O(NlogN)的時間復雜度,缺點是Merge的時候要有O(N)的額外的空間,如何改進?

使用原地歸並排序 In-place Merge Sort: http://www.ahathinking.com/archives/103.html

//reverse array
void reverse(int arr[], int size)
{
    int left = 0;
    int right = size -1;
    while(left < right)
    {
        int temp = arr[left];
        arr[left++] = arr[right];
        arr[right--] = temp;
    }
}

// swap [arr,arr+headSize) and [arr+headSize,arr+headSize+endSize)
void SwapMemory(int arr[], int headSize, int endSize)
{
    reverse(arr, headSize);
    reverse(arr + headSize, endSize);
    reverse(arr, headSize + endSize);
}

//原地歸並
void Inplace_Merge(int arr[], int beg, int mid, int end)
{
    int i = beg;     // 指示有序串1
    int j = mid + 1; // 指示有序串2
    while(i < j && j <= end)
    {
        while(i < j && arr[i] <= arr[j])
        {
            ++i;
        }
        int index = j;
        while(j <= end && arr[j] <= arr[i])
        {
            ++j;
        }
        SwapMemory(&arr[i], index-i, j-index);//swap [i,index) and [index,j)
        i += (j-index);
    }
}

//原地歸並排序
void Inplace_MergeSort(int arr[], int beg, int end)
{
    if(beg < end)
    {
        int mid = (beg + end) / 2;
        Inplace_MergeSort(arr, beg, mid);
        Inplace_MergeSort(arr, mid+1, end);
        Inplace_Merge(arr, beg, mid, end);
    }
}

 

An example of merge sort. First divide the list into the smallest unit (1 element), then compare each element with the adjacent list to sort and merge the two adjacent lists. Finally all the elements are sorted and merged.

 

參考:

http://en.wikipedia.org/wiki/Sorting_algorithm

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM