1. 程式人生 > >經典排序算法 — C# 版(上)

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

方式 選擇排序 可能 width bubble 插入 表示 == eight

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

經典的排序算法又非常多,我們怎麽評價一個排序算法的好壞呢?

其實可以這樣想,要細致的比較排序算法好壞,那我們就從多方面盡可能詳細的對比

一、效率方面

1、排序算法的執行效率:最好、最壞、平均

2、 我們之前舍棄的時間復雜度的系數、常量、低階,在這裏需要拿回來

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

二、內存消耗方面

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

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

原地排序 = O(1)

三、算法穩定性

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

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

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

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

1、冒泡排序

圖例演示

技術分享圖片

> 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:是,因為沒有改變相同元素的先後順序。

2、插入排序

假定,我們將排序串分為兩個區:已排序區,未排序區

一個元素要找到正確的的位置進行插入,那麽需要去已排序區域找到自己的位置後,

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

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

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

比如: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 會有很大的差距。

我們的編程語言中的排序算法很多都會傾向於插入排序算法。

3、選擇排序

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

所以也分為 已排序區和未排序區,操作方式是在未排序區間找到最小的,然後放到已排序區間最後。

圖例:

技術分享圖片

> 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:不是

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

分別執行時間如下:

技術分享圖片

技術分享圖片

技術分享圖片

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