1. 程式人生 > >基於排序的演算法

基於排序的演算法

 以前有句話說程式==演算法+資料結構,其實不然,如果說程式設計像是在蓋樓房,那麼學好資料結構和演算法,就是在打下一個完美的地基。

        所以今天閒著無聊,給大家總結總結,幾個非常非常基礎的演算法,這在許多公司的筆試,面試中都要可能出現。今天我所要想討論的是排序演算法,說到排序演算法,大家在熟悉不過的是氣泡排序演算法,在我後來接觸的一些列排序演算法中,後來想想每個演算法都有其巧妙之處,也不單單是為了追求的時間上的效率。下面我就來列出主要的幾種排序演算法。

1.氣泡排序演算法,平均時間複雜度o(n2),作為一種效能還算不錯的排序演算法

2.選擇排序演算法

3.插入排序演算法

4.歸併排序演算法

5.桶排序演算法

6.基數排序演算法

7.希爾排序演算法

8.堆排序演算法,其中涉及到了一些資料結果中的一些的知識

9.快速排序演算法,快速排序演算法,在此類中的 排序效能中算是最好吧,但是如果一組數字基本都是有序的情況的時候,快速排序就又會被退化為氣泡排序,基本排序演算法差差不多都列在上面了,具體掌握上述中排序演算法的精髓,對於面試還是很有幫助的

----------------------------------------------------------


問題:設有一陣列,其大小為10個元素(int   str[10])陣列內的資料是無序。現在要求我們通過程式設計將這個無序的陣列變成一個從小到大排序的陣列(從下標為0開始)

思路:按照題目的要求,毫無疑問,正確的結果應該就像這樣: 1 2 3 4 5 6 7 8 9 10   要做到這樣,最簡單和最直接想到的方法就是進行對比交換。

首先,把10個數裡最小的個數放到下標為0的位置上(str[0])
通過將下標為0的數(str[0])與剩下其餘9個數進行對比交換(將較少者放置在下標為0的位置上),就可以得到這10個數最小的那個
10個數最小的那位確定後,接下來就要找剩下9個數最小的那個。
因為已經確定出一個最小的數,所以就不要動str[0],直接從str[1]開始,與剩下的8個數對比交換,找出9個數中最小的那位放到下標為1(str[1])的位置上
繼續按照這個思路就可以將這十個數變成有序的(從小到大)的陣列
程式碼:
[cpp] view plaincopy

#include <stdio.h>  
  
void swap(int *a, int *b); //交換兩個數  
  
int main()  
{  
    int     str[10];  
    int     i, j;  
    //初始化陣列為10 9 8 7 6 5 4 3 2 1  
    for (i = 0; i < 10; i++)  
    {  
        str[i] = 10 - i;  
    }  
    //排序,從a[0]開始排,從小到大  
    for (i = 0; i < 10; i++)  
    {  
        for (j = i + 1; j < 10; j++)  
        {  
            if (str[i] > str[j])  
            {  
                swap(&str[i], &str[j]);  
            }  
        }  
    }  
        //將十個數輸出  
    for (i = 0; i < 10; i++)  
        printf("%d\n", str[i]);  
    return    0;  
}  
void swap(int *a, int *b)  
{  
    int     c;  
     c = *a;  
    *a = *b;  
    *b =  c;  
}  

這個方法是比較容易想到的實現方法。但存在不足:就是本來位於前面的較小數被交換到後面

演示:
開始:9 4 5 6 8 3 2 7 10 1  (下標從左到右分別是0~9)按照上面的程式進行對比交換
第一次:4 9 5 6 8 3 2 7 10 1 
第二次:4 9 5 6 8 3 2 7 10 1 
。。。:(沒有交換)
第五次:3 9 5 6 8 4 2 7 10 1 
第六次:2 9 5 6 8 3 4 7 10 1 
。。。:(沒有交換)
第十次:1 9 5 6 8 3 4 7 10 2 

可以看出,原來較小的數是在前面的,經過一輪的交換後放到後面了

那麼怎樣解決這個不足呢?可以使用氣泡排序
什麼是氣泡排序呢?
你可以這樣理解:(從小到大排序)存在10個不同大小的氣泡,由底至上地把較少的氣泡逐步地向上升,這樣經過遍歷一次後,最小的氣泡就會被上升到頂(下標為0),然後再從底至上地這樣升,迴圈直至十個氣泡大小有序。
在氣泡排序中,最重要的思想是兩兩比較,將兩者較少的升上去
氣泡排序最壞情況的時間複雜度是O(n²)

程式碼:
[cpp] view plaincopy

#include <stdio.h>  
void swap(int *a, int *b);  
int main()  
{  
    int    array[10] = {15, 225, 34, 42, 52, 6, 7856, 865, 954, 10};  
    int    i, j;  
    for (i = 0; i < 10; i++)  
    {  
        //每一次由底至上地上升  
        for (j = 9; j > i; j--)  
        {  
            if (array[j] < array[j-1])  
            {  
                    swap(&array[j], &array[j-1]);  
            }  
        }  
    }  
    for (i = 0; i < 10; i++)  
    {  
        printf("%d\n", array[i]);  
    }  
    return    0;  
}  
void swap(int *a, int *b)  
{  
    int    temp;  
    temp = *a;  
      *a = *b;  
      *b = temp;  
}  
-----====================================================
選擇排序演算法
假定存在陣列 array[0..n-1], 選擇排序的核心思想是:

第 i 趟排序是從後面的 n - i + 1(i = 1,2,3,4,. . .,n - 1)個元素中選擇一個值最小的元素與該 n - i + 1 個元素的最前門的那個元素交換位置,即與整個序列的第 i 個元素交換位置。如此下去,直到 i = n - 1,排序結束。

也可描述為:

每一趟排序從序列中未排好序的那些元素中選擇一個值最小的元素,然後將其與這些未排好序的元素的第一個元素交換位置。

特點:

1. 演算法完成需要 n - 1 趟排序,按照演算法的描述,n - 1 趟排序之後陣列中的前 n - 1 個元素已經處於相應的位置,第 n 個元素也處於相應的位置上。

2. 第 i 趟排序,實際上就是需要將陣列中第 i 個元素放置到陣列的合適位置,這裡需要一個臨時變數 j 來遍歷序列中未排好序的那些元素,另一臨時變數 d 來記錄未排好序的那些元素中值最小的元素的下標值,

3. 一趟遍歷開始時,令 d = i,假定未排序序列的第一個元素就是最小的元素,遍歷完成後,變數 d 所對應的值就是值最小的元素,判斷 d 是否是未排序序列的第一個元素,如果是,則不需要交換元素,如果不是,則需要交換array[d] 和 array[i]。

4. 此方法是不穩定排序演算法,可對陣列{a1 = 49,a2 = 38, a3 = 65, a4 = 49, a5 = 12, a6 = 42} 排序就可以看出,排序完成後 a1 和 a4的相對位置改變了。

5. 此方法移動元素的次數比較少,但是不管序列中元素初始排列狀態如何,第 i 趟排序都需要進行 n - i 次元素之間的比較,因此總的比較次數為

1 + 2 + 3 + 4 +5 + . . . + n - 1 = n(n-1)/2, 時間複雜度是 O(n^2).

看程式碼:

[cpp] view plaincopy

#include <stdlib.h>  
#include <stdio.h>  
  
void selectSort(int array[], int n)  
{  
    int i, j, d;  
    int temp;  
    for(i = 0; i < n - 1; ++i)  
    {  
        d = i;   //開始一趟選擇排序,假定第i個元素是後面n - i + 1個未排序的元素中最小的元素  
        for(j = i + 1; j < n; ++j)  
            if(array[j] < array[d])  //如果發現比當前最小元素還小的元素,則更新記錄最小元素的下標d  
                d = j;  
  
        if(d != i)   //如果最小元素的下標不是後面n - i + 1的未排序序列的第一個元素,則需要交換第i個元素和後面找到的最小元素的位置  
        {  
            temp = array[d];  
            array[d] = array[i];  
            array[i] = temp;  
        }  
    }  
}  
  
int main()  
{  
    int array[] = {3, 1, 15, 11, 89, 5};  
    int size = sizeof(array)/sizeof(int);  
    selectSort(array, size);  
  
    for(int i = 0; i < size; ++i)  
    {  
        printf("%d ", array[i]);  
    }  
    printf("\n");  
}  
=======================================================
插入排序演算法
經典排序演算法 – 插入排序Insertion sort  
插入排序就是每一步都將一個待排資料按其大小插入到已經排序的資料中的適當位置,直到全部插入完畢。 
插入排序方法分直接插入排序和折半插入排序兩種,這裡只介紹直接插入排序,折半插入排序留到“查詢”內容中進行。 
  圖1演示了對4個元素進行直接插入排序的過程,共需要(a),(b),(c)三次插入。

以下程式碼僅供參考,歡迎指正


        /// <summary>
        /// 插入排序
        /// </summary>
        /// <param name="unsorted"></param>
        static void insertion_sort(int[] unsorted)
        {
            for (int i = 1; i < unsorted.Length; i++)
            {
                if (unsorted[i - 1] > unsorted[i])
                {
                    int temp = unsorted[i];
                    int j = i;
                    while (j > 0 && unsorted[j - 1] > temp)
                    {
                        unsorted[j] = unsorted[j - 1];
                        j--;
                    }
                    unsorted[j] = temp;
                }
            }
        }

        static void Main(string[] args)
        {
            int[] x = { 6, 2, 4, 1, 5, 9 };
            insertion_sort(x);
            foreach (var item in x)
            {
                if (item > 0)
                    Console.WriteLine(item + ",");
            }
            Console.ReadLine();
        }


=================================
歸併排序演算法
經典排序演算法 - 歸併排序Merge sort

原理,把原始陣列分成若干子陣列,對每一個子陣列進行排序,

繼續把子陣列與子數組合並,合併後仍然有序,直到全部合併完,形成有序的陣列

舉例

無序陣列[6 2 4 1 5 9]

先看一下每個步驟下的狀態,完了再看合併細節

第一步 [6 2 4 1 5 9]原始狀態

第二步 [2 6] [1 4] [5 9]兩兩合併排序,排序細節後邊介紹

第三步 [1 2 4 6] [5 9]繼續兩組兩組合並

第四步 [1 2 4 5 6 9]合併完畢,排序完畢

輸出結果[1 2 4 5 6 9]

合併細節

詳細介紹第二步到第三步的過程,其餘類似

第二步:[2 6] [1 4] [5 9]

兩兩合併,其實僅合併[2 6] [1 4],所以[5 9]不管它,

原始狀態

第一個陣列[2 6]

第二個陣列[1 4]

--------------------

第三個陣列[...]

 

第1步,順序從第一,第二個數組裡取出一個數字:2和1

比較大小後將小的放入第三個陣列,此時變成下邊這樣

第一個陣列[2 6]

第二個陣列[4]

--------------------

第三個陣列[1]

 

第2步,繼續剛才的步驟,順序從第一,第二個數組裡取資料,2和4,

同樣的比較大小後將小的放入第三個陣列,此時狀態如下

第一個陣列[6]

第二個陣列[4]

--------------------

第三個陣列[1 2]

 

第3步,再重複前邊的步驟變成,將較小的4放入第三個陣列後變成如下狀態

第一個陣列[6]

第二個陣列[...]

--------------------

第三個陣列[1 2 4]

 

第4步,最後將6放入,排序完畢

第一個陣列[...]

第二個陣列[...]

--------------------

第三個陣列[1 2 4 6]

 

[ 1 2 4 6 ]與[ 5 9 ]的合併過程與上邊一樣,不再分解

程式碼僅供參考


        static void merge(int[] unsorted, int first, int mid, int last, int[] sorted)
        {
            int i = first, j = mid;
            int k = 0;
            while (i < mid && j < last)
                if (unsorted[i] < unsorted[j])
                    sorted[k++] = unsorted[i++];
                else
                    sorted[k++] = unsorted[j++];

            while (i < mid)
                sorted[k++] = unsorted[i++];
            while (j < last)
                sorted[k++] = unsorted[j++];

            for (int v = 0; v < k; v++)
                unsorted[first + v] = sorted[v];
        }

        static void merge_sort(int[] unsorted, int first, int last, int[] sorted)
        {
            if (first + 1 < last)
            {
                int mid = (first + last) / 2;
                Console.WriteLine("{0}-{1}-{2}", first, mid, last);
                merge_sort(unsorted, first, mid, sorted);
                merge_sort(unsorted, mid, last, sorted);
                merge(unsorted, first, mid, last, sorted);
            }
        }

        static void Main(string[] args)
        {
            int[] x = { 6, 2, 4, 1, 5, 9 };
            int[] sorted = new int[x.Length];
            merge_sort(x, 0, x.Length, sorted);
            for (int i = 0; i < sorted.Length; i++)
            {
                if (x[i] > 0)
                    Console.WriteLine(x[i]);
            }
            Console.ReadLine();
        }


基本排序演算法的時間複雜度分析
1、插入排序    最佳情況:O(n)    最差情況:O(nlgn)    平均情況:O(nlgn)2、選擇排序    在所有情況下的時間複雜度均為O(n*n)3、氣泡排序    最佳情況:O(n)...
--------------------- 
原文:https://blog.csdn.net/qq_26562641/article/details/50344443