經典排序算法 — C# 版(上)

提起排序,與咱們的息息相關,平時開發的代碼少不了排序。算法

經典的排序算法又很是多,咱們怎麼評價一個排序算法的好壞呢?編程

其實能夠這樣想,要細緻的比較排序算法好壞,那咱們就從多方面儘量詳細的對比數組

 

1、效率方面

一、排序算法的執行效率:最好、最壞、平均編程語言

二、 咱們以前捨棄的時間複雜度的係數、常量、低階,在這裏須要拿回來spa

三、排序,免不了比較和移動3d

 

2、內存消耗方面

沒錯就是 算法的空間複雜度,不過對於排序的空間複雜度來講,又賦予了新的名詞 — 原地排序。code

顧名思義是 原地排序的確定是消耗內存少,反之須要往外走幾步那就須要臨時申請內存了。對象

原地排序 = O(1)blog

 

3、算法穩定性

字面意義就是不論怎麼擺弄,這個算法穩定,不會對順序有影響。排序

上面這句話應該加上一個定語:對於擁有相同值的元素的先後順序不會發生改變。

舉個例子:有兩個對象,其中的金額字段同樣,按照金額排序,通過算法一頓折騰後,相同金額的對象前後順序不能發生改變。

 

講完評估排序算法的優劣的幾個方面,那就直接看看咱們平時常見的幾個經典算法:

一、冒泡排序

圖例演示

> C#

 1          //排序 — 冒泡排序
 2         private static void BubbleSort(int[] source)
 3         {
 4             if (source.Length <= 1)
 5                 return;
 6 
 7             bool isChanged = false;
 8             for (int i = 0; i < source.Length; i++)
 9             {
10                 for (int j = 0; j < source.Length - i - 1; j++)
11                 {
12                     var left = source[j];
13                     var right = source[j + 1];
14                     Console.WriteLine("【比較】");
15                     if (left <= right)
16                         continue;
17 
18                     source[j] = right;
19                     source[j + 1] = left;
20                     isChanged = true;
21                     Console.WriteLine("{交換}");
22                 }
23                 if (!isChanged)
24                     break;
25             }
26             Printf(source);
27         }

 

Q:冒泡排序的時間算法複雜度

A:最壞時間複雜度 — O(n^2):循環 n*n次

   最好時間複雜度 — O(n)    :循環 n次便可

   平均時間複雜度 — O(?)

           這裏咱們使用機率來分析平均複雜度,狀況比較複雜。

   咱們使用一種新的概念來分析平均複雜度,這個就是 有序度。

           有序度:看做是向量,左<= 右

      逆序度:正好相反,左 >= 右

   滿有序度 = n*(n-1) / 2

   逆序度 = 滿有序度 - 有序度

 

對於 n 個數據來講,最壞狀況時間複雜度的有序度是0,要交換 n*(n-1)/2次才能正確輸出。

對於最好狀況複雜度的有序度是n*(n-1)/2,須要交換0次就能達到徹底有序。

最壞 n*(n-1)/2次,最好0次,取箇中間值來表示中間狀況,也能夠看做是平均狀況 n*(n-1) /4

因此平均下來 要作 n*(n-1) / 4 次纔能有序,由於冒泡排序的時間複雜度的上限是 O(n^2)

因此平均狀況時間複雜度爲 O(n^2)

雖然這樣推論平均個狀況並不嚴格,可是比起機率推論來講,這樣簡單且有效。

 

Q:冒泡排序是否是原地排序

A:是,臨時變量爲了交換數據,常量級別的臨時空間申請,因此空間複雜度爲O(1)

 

Q:冒泡排序是否是穩定排序

A:是,由於沒有改變相同元素的前後順序。

 

 二、插入排序

假定,咱們將排序串分爲兩個區:已排序區,未排序區

一個元素要找到正確的的位置進行插入,那麼須要去已排序區域找到本身的位置後,

將這個位置的元素們向後移動,空出位置,而後新元素入坑。

從以上這個思路來看,插入排序也是涉及到了元素的比較和移動。

給咱們一個無序數組,哪塊是已排序區?哪裏是未排序區?

 

好比:9, 0, 1, 5, 2, 3, 6

初始時,9 就是已排序區域;

 0開始去已排序區域挨個比較,即 i=1,0<9,9向後挪動,空出位置,0入坑;

1開始去 [ 0,9 ] 已排序區域比較,1 < 9,9向後移動騰位置,1入坑,1 > 0 無需操做;

依次重複以上操做,便可達成有序。

 

圖例演示

 

> C#

 1         //排序 — 插入排序
 2         private static void InsertionSort(int[] source)
 3         {
 4             if (source == null || source.Length <= 0)
 5                 return;
 6 
 7             for (int i = 1; i < source.Length; i++)
 8             {// 未排序區
 9                 var sorting = source[i];
10                 int j = i - 1;
11 
12                 for (; j >= 0; j--)
13                 {// 已排序區
14 
15                     // 比較
16                     if (sorting >= source[j])
17                     {
18                         break;
19                     }
20 
21                     // 後移
22                     source[j + 1] = source[j]; 23                 }
24 
25                 // 入坑
26                 source[j + 1] = sorting; 27             }
28             Printf(source);
29         }

 

Q:插入排序的時間算法複雜度

A:最壞時間複雜度 — O(n^2):徹底倒序,循環n次,比較n次

   最好時間複雜度 — O(n):徹底有序,循環n次跳出

   平均時間複雜度 — O(n^2):循環 n次數據,在一個數組中插入數據的平均狀況時間複雜度爲O(n),因此是 O(n^2)

 

Q:插入排序是否是原地排序

A:是,沒有臨時變量申請,因此空間複雜度爲O(1)

 

Q:插入排序是否是穩定排序

A:是, if (sorting >= source[j]) 這個判斷保證了相同元素的前後順序不變,

         去掉等於號也能夠發生改變。能夠實現穩定排序因此說是穩定排序

 

開始咱們也說了,這麼多排序算法,咱們要對比一下,擇優選擇。

排序 最好狀況 最壞狀況 平均狀況 是否穩定 是否原地
冒泡 O(n) O(n^2) O(n^2)
插入 O(n) O(n^2) O(n^2)

 

 

 

 

那麼問題來了平均都是 O(n^2),爲何傾向於使用插入排序呢?

這兩種排序咱們將常量都放進來會發現,冒泡使用的常量數比排序多,因此在數據量上來後  常量*n 會有很大的差距。

咱們的編程語言中的排序算法不少都會傾向於插入排序算法。

 

三、選擇排序

其實操做相似於插入排序,只不過是換了換操做方式。

因此也分爲 已排序區和未排序區,操做方式是在未排序區間找到最小的,而後放到已排序區間最後。

圖例:

 

> C#

        private static void SelectionSort(int[] source)
        {
            if (source.Length <= 1)
                return;

            for (int i = 0; i < source.Length - 1; i++)
            {// 已排序
                var minIndex = i;
                
                for (int j = i+1; j < source.Length; j++)
                {//未排序

                    if (source[minIndex] > source[j])
                    {
                        minIndex = j;
                    }
                }
                if (i != minIndex)
                {
                    int tmp = source[i];
                    source[i] = source[minIndex];
                    source[minIndex] = tmp;
                }
            }

            Printf(source);
        }

 

Q:選擇排序的時間算法複雜度

A:最壞時間複雜度 — O(n^2)

   最好時間複雜度 — O(n^2)

   平均時間複雜度 — O(n^2)

 

Q:選擇排序是否是原地排序

A:是,沒有臨時變量申請,因此空間複雜度爲O(1)

 

Q:選擇排序是否是穩定排序

A:不是

 

四、對比 隨機生成1000個元素的 int 數組

分別執行時間以下:

相關文章
相關標籤/搜索