(轉載)那些年咱們一塊兒學過的「排序算法」

(轉載) http://www.cnblogs.com/BeyondAnyTime/archive/2012/08/14/2638070.html

排序算法是常常使用的算法,在STL中也有一個比較牛X的快速排序(sort),可是咱們不能只會調用sort呀!?做爲一個好學的同窗,咱們要知道各類排序的內部是怎麼實現滴~~~提到排序算法咱們要知道兩個常常提到的概念:html

(1)排序算法的穩定性:所謂「穩定性」是指,在待排序數組出現的兩個相同的元素,排序以後相對維持保持不變。好比:待排序數組爲arr[] = {1,4,3,1},排序以後元素變爲arr_new[] = {1,1,4,3},而且arr_new中的第一個是arr中的第一個1,arr_new中的第二個1是arr中的第二個1,這是咱們就說這種排序時穩定的。ios

(2)原地排序:所謂原地排序是指,不申請多餘的空間來輔助完成排序算法,而是在原來的待排序的數據之上直接進行比較,交換,移動等操做。算法

1.插入排序數組

算法原理:將待排序的數組分爲:有序區 和 無序區。而後每次從無序區取出第一個數據插入到有序區的正確位置,最終完成排序。學習

算法代碼:優化

#include <iostream>

using namespace std;

void insert_sort(int *arr,int n)
{
    int i,j;
    for(i = 1 ; i < n ; ++i)
    {
        int tmp = arr[i];
        j = i - 1;
        while( j >= 0 && arr[j] > tmp)
        {
            arr[j+1] = arr[j];
            j--;
        }
        arr[j+1]  = tmp;
    }
}

int main()
{
    int arr[] = {2,4,1,3,5,8,7,6,8};
    insert_sort(arr,9);
    for(int i = 0  ; i < 9 ; ++i)
    {
        cout<<arr[i]<<" ";
    }
    cout<<endl;
    return 0;
}

小結:看代碼能夠知道這種排序算法的時間複雜度是O(n^2),而且插入排序時穩定的,屬於原地排序。那麼何時使用插入排序比較好呢?那就是當數組中的大部分數據已經有序時,使用插入排序算法的效率比較高,這種狀況下,所須要進行的數據移動較少,而數據移動正式插入排序算法的主要步驟~~~~ui

2.冒泡排序spa

算法原理:冒泡排序是通過n-1趟子排序完成的,第 i 趟子排序從第1個數至第 n-i+1 個數,若第 i 個數比第 i+1 個數大,則交換這兩個數,實際上這樣通過 i 次子排序就使得 第1個數至第 n-i +1個數之間最大的數交換到了n-i+1 的位置上了。實際上冒泡排序時能夠優化的,那就是當第 i 次子排序並無發生元素的交換時,就說明數組已經排好序了,之後的子排序就不用作了。code

算法代碼:htm

#include <iostream>

using namespace std;

void swap(int &x,int &y)
{
    x = x^y;
    y = x^y;
    x = x^y;
}

void bubble_sort(int *arr,int n)
{
    int i,j;
    for(i = n - 1 ; i > 0 ; --i)
    {
        bool flag = true;
        for(j = 0 ; j < i ; ++j)
        {
            if(arr[j] > arr[j+1])
            {
                flag = false;
                swap(arr[j],arr[j+1]);
            }
        }
        if(flag) //數組已經排好序不必在繼續進行其餘子排序了
            break;
    }
}

int main()
{
    int arr[] = {2,1,4,3,8,7,5,6};
    bubble_sort(arr,8);
    for(int i = 0 ; i < 8 ; ++i)
    {
        cout<<arr[i]<<" ";
    }
    cout<<endl;
    return 0;
}

小結:冒泡排序算法的時間複雜度是O(n^2),同時冒泡排序也是穩定的,而且屬於原地排序,排序的效率取決於逆序對的多少。採用一點小優化也加速了冒泡排序。

3.選擇排序

算法原理:所謂選擇排序通過 n-1 次選擇,當進行第 i 次選擇時,是從第1個元素到第 n-i+1 的元素中選擇最大的元素和第 n-i+1 個位置的元素交換,這樣作好比第1 次選擇使得最大的元素到了數組的最後一個位置。注意哦,在選擇排序中每次選擇時只進行一次數據的交換。

算法代碼:

#include <iostream>

using namespace std;

void swap(int &x,int &y)
{
    int tmp = x;
    x = y;
    y = tmp;
}

void select_sort(int *arr,int n)
{
    int i,j;
    for(i = n-1 ; i > 0 ; --i)
    {
        int tmp = 0;
        for(j = 1 ; j <= i ; ++j)
        {
            if(arr[j] >= arr[tmp])//這裏的「=」是保證選擇排序穩定的關鍵
            {
                tmp = j;
            }
        }
        swap(arr[i],arr[tmp]);
    }
}
int main()
{
    int arr[] = {2,1,4,3,8,7,5,6};
    select_sort(arr,8);
    for(int i = 0 ; i < 8 ; ++i)
    {
        cout<<arr[i]<<" ";
    }
    cout<<endl;
    return 0;
}

小結:選擇排序的思路很是的簡單,實現起來也不難。時間複雜度是O(n^2),選擇排序也是穩定的排序,而且也是原地排序。選擇排序的時間基本不受數據的影響,由於無論怎樣都要進行n-1次選擇排序。

4.歸併排序

算法原理:歸併排序的思想是分治,將一個帶排序的數組分紅兩個較小的數組,而後分別進行排序,組後將兩個排好序的較小的數組合並起來,就獲得了原來數組的排序後的結果。應該注意的是這種將兩個排好序的數組合並有一個較好的算法,時間複雜度是O(n1+n2)的。n一、n2分別是兩個小數組的長度。

算法代碼:

#include <iostream>

using namespace std;

void merge_sort(int *arr,int start,int end,int *temp)
{
    if(end > start+1)
    {
        int mid = start + (end - start) / 2;
        merge_sort(arr,start,mid,temp);
        merge_sort(arr,mid,end,temp);
        int i = start , j = mid , k = start;
        while(i < mid || j < end)
        {
            if(j >= end || (i < mid && arr[i] <= arr[j]))
            {
                temp[k++] = arr[i++];
            }
            else
            {
                temp[k++] = arr[j++];
            }
        }
        for(i = start ; i < end ; ++i)
        {
            arr[i] = temp[i];
        }
    }
}


int main()
{
    int temp[8];
    int arr[]  = {2,1,4,3,8,7,5,6};
    merge_sort(arr,0,8,temp);
    for(int i = 0 ; i < 8 ; ++i)
    {
        cout<<arr[i]<<" ";
    }
    cout<<endl;
    return 0;
}

小結:歸併排序時穩定的排序,可是不屬於原地排序,由於用了額外的O(n)的空間,時間複雜度降到了O(n*log n),而且對任意的數組進行排序時間複雜度都能控制在O(n*logn)。

5.堆排序

算法原理:所謂的堆排序是利用徹底二叉樹的思想實現的。首先應該提到的是最大堆,在最大堆中(徹底二叉樹二叉樹)中每一個父節點都大於等於兩個兒子節點的值,這時候很明顯堆頂是元素的最大值,而後把堆頂元素和堆中最後一個元素(分層遍歷的節點編號最大的元素)交換,這樣最大值就落到了數組的arr[n-1]的位置,而後把前n-1元素繼續按照上面的方式處理,如此進行n-1次就完成堆排序。

算法代碼:

#include <iostream>

using namespace std;

void swap(int &x,int &y)
{
    x = x + y;
    y = x - y;
    x = x - y;
}

void quick_sort(int *arr,int s,int e)
{
    if(s+1 < e)
    {
        int tmp = arr[s];
        int i = s+1;
        int j = e-1;
        while(i < j)
        {
            while(i <= j && arr[i] <= tmp)
            {
                i++;
            }
            while(i <= j && arr[j] >= tmp)
            {
                j--;
            }
            if(i < j)
            {
                swap(arr[i],arr[j]);
            }
        }
        swap(arr[s],arr[i-1]);
        quick_sort(arr,s,i-1);
        quick_sort(arr,i,e);
    }
}

int main()
{
    int arr[] = {2,1,4,3,8,7,5,6};
    quick_sort(arr,0,8);
    for(int i = 0 ; i < 8 ; ++i)
    {
        cout<<arr[i]<<" ";
    }
    cout<<endl;
    return 0;
}

小結:堆排序是不穩定的排序,可是堆排序屬於原地排序。時間複雜度是O(n*log n),而且不須要額外的輔助空間,也就是說堆排序是一種不錯的排序算法哦~~~

6.快速排序

算法原理:快速排序時這樣的一種排序,選取數組中的第一個元素arr[0]做爲依據,遍歷一遍數組後,使得數組中的第一個元素進入正確的位置,即在該位置左面的元素均小於等於arr[0],在該位置右面的元素均大於等於arr[0]。而後,在對該位置左面和右面的元素分別進行快速排序,如此一來完成整個數組的排序。

算法代碼:

#include <iostream>

using namespace std;

void swap(int &x,int &y)
{
    x = x + y;
    y = x - y;
    x = x - y;
}

void quick_sort(int *arr,int s,int e)
{
    if(s+1 < e)
    {
        int tmp = arr[s];
        int i = s+1;
        int j = e-1;
        while(i < j)
        {
            while(i <= j && arr[i] <= tmp)
            {
                i++;
            }
            while(i <= j && arr[j] >= tmp)
            {
                j--;
            }
            if(i < j)
            {
                swap(arr[i],arr[j]);
            }
        }
        swap(arr[s],arr[i-1]);
        quick_sort(arr,s,i-1);
        quick_sort(arr,i,e);
    }
}

int main()
{
    int arr[] = {2,1,4,3,8,7,5,6};
    quick_sort(arr,0,8);
    for(int i = 0 ; i < 8 ; ++i)
    {
        cout<<arr[i]<<" ";
    }
    cout<<endl;
    return 0;
}

小結:首先仍是說明快速排序時不穩定的,可是是原地排序,不須要額外的空間,時間複雜度是O(nlog n),實際上,這種把第一個元素做爲依據元素只是快速排序的一種,STL中的sort內部實現是根據排序到了不一樣的階段選用不一樣的排序算法。當數據量大是採用quick_sort排序,當分段遞歸到了數據量小於某個數值時,爲避免quick_sort的遞歸調用帶來的額外開銷,就改用insert_sort 了;若是遞歸層次過深,還會考慮使用heap_sort 。

 


學習中的一點總結,歡迎拍磚哦^^

相關文章
相關標籤/搜索