程式猿修仙之路--演算法之直接插入排序

架構師修行之路發表於2019-04-12
程式猿修仙之路--演算法之直接插入排序

==演算法主要衡量標準==


  • 時間複雜度(執行時間)

在演算法時間複雜度維度,我們主要對比較和交換的次數做對比,其他不交換元素的演算法,主要會以訪問陣列的次數的維度做對比。

其實有很多同學對於演算法的時間複雜度有點模糊,分不清什麼所謂的 O(n),O(nlogn),O(logn)...等,也許下圖對一些人有一些更直觀的認識。

image

  • 空間複雜度(額外的記憶體使用)

排序演算法的額外記憶體開銷和執行時間同等重要。 就算一個演算法時間複雜度比較優秀,空間複雜度非常差,使用的額外記憶體非常大,菜菜認為它也算不上一個優秀的演算法。

  • 結果的正確性

這個指標是菜菜自己加上的,我始終認為一個優秀的演算法最終得到的結果必須是正確的。就算一個演算法擁有非常優秀的時間和空間複雜度,但是結果不正確,又有什麼意義呢?

==原理==


每次在無序的列表中取一個元素插入到一個有序列表的適當位置,成為一個元素加1的新的有序列表。

插入排序根據原理又分為 直接插入排序、二分插入排序、希爾排序等,今天主要講一下直接插入排序。直接插入排序是一種穩定的排序演算法

假設排序順序從左至右,具體步驟如下:

  1. 列表第一個元素和前面元素比較,如果小於前面元素(其實不存在),則交換位置。(這步其實可以沒有)
  2. 列表第二個元素和前面元素(第一個元素)比較,如果小於前面元素,則交換位置。
  3. 列表第三個元素和前面元素(第二個元素)比較,如果小於前面元素,則交換位置。如果和前面元素交換了位置,現在在第二個位置上,則接著繼續和前面元素比較(第一個元素),如果小於前面元素,接著再次交換位置,然後再次重複比較過程....

...繼續重複以上過程,直到最後一個元素完成比較

比較移動過程中,如果元素不需要移動意味著該元素排序完畢。

++網路上的插入排序大多都是新建一個有序列表用來存放最終結果,其實在無序列表上進行排序操作空間複雜度才更優++

也許一張更直觀的圖比上千句話效果都好:

image

==複雜度==

  • 時間複雜度
  1. 比較次數

對於長度為N的主鍵不重複的列表,插入排序 平均情況下需要n²/4次比較,最壞情況下需要n²/2次比較,最好的情況下需要n-1 次比較。

  1. 交換次數

對於長度為N的主鍵不重複的列表,插入排序平均情況下需要n²/4次交換,最壞情況下需要n²/2次交換,最好情況下需要0次交換。

==效能和特點==


總體來說,直接插入排序是一種比較簡單的排序演算法,很容易理解也很好用程式碼實現,當然他的特點也很明顯:

執行時間和資料初始狀態有關

插入排序的思想是把一個元素插入一個有序的列表中,假如這個元素的位置正好是有序部分的末尾呢?也就是說當前元素不用移動位置。 再一次假如整個列表都是有序的會發生什麼情況呢?根本就不需要移動任何元素。這也就是為什麼在最好的情況下交換次數為0,比較次數為n-1的原因。

假如列表的很大一部分元素是有序的,插入排序可能比大多數排序演算法都要快。

==適用場景==

直接插入排序對於小型列表或者非隨機元素列表很有效。例如:部分元素有序。大體可歸納為:

  1. 每個元素距離自己的最終位置都不遠。
  2. 一個有序的大列表連線一個小列表。
  3. 列表中只有少數元素不正確。

==其他==

  • 為什麼插入排序是穩定呢?

插入排序是在一個已經有序的小序列的基礎上,一次插入一個元素。當然,剛開始這個有序的小序列只有1個元素,就是第一個元素。比較是從有序序列的末尾開始,也就是想要插入的元素和已經有序的最大者開始比起,如果比它大則直接插入在其後面,否則一直往前找直到找到它該插入的位置。如果碰見一個和插入元素相等的,那麼插入元素把想插入的元素放在相等元素的後面。所以,相等元素的前後順序沒有改變,從原無序序列出去的順序就是排好序後的順序,所以插入排序是穩定的。

==實現案例==

  • c#
         static void Main(string[] args)
        {
            List<int> data = new List<int>() ;
            for (int i = 0; i < 10; i++)
            {
                
                data.Add(new Random(Guid.NewGuid().GetHashCode()).Next(1, 100));
            }
            //列印原始陣列值
            Console.WriteLine($"原始資料: {string.Join(",", data)}");
            int n = data.Count;
            //此處可以直接從第二個元素開始
            for (int i = 1; i < n; i++)
            {               
                //查詢最小的元素的索引
                for (int j = i; j>0  ; j--)
                {
                    if (data[j] < data[j - 1])
                    {
                        //異或法 交換兩個變數,不用臨時變數
                        data[j] = data[j] ^ data[j-1];
                        data[j-1] = data[j] ^ data[j - 1];
                        data[j ] = data[j] ^ data[j - 1];
                    }
                    
                }

            }
            //列印排序後的陣列
            Console.WriteLine($"排序資料: {string.Join(",", data)}");
            Console.Read();
        }
複製程式碼

執行結果:

原始資料: 72,78,42,60,84,74,60,79,72,52

排序資料: 42,52,60,60,72,72,74,78,79,84

Go

家裡沒環境,還得翻牆,以後再補上吧,望見諒。


獨樂不如眾樂

我表弟在追學校的一個女生,每天簡訊無數,可那妞從來都不回他。我對他說:騷年!女人的天性只是八卦和好奇心!就你這樣還想泡妞呢!看你表哥的!我用他手機給那妞發:你是我們學校三大美女之一,但我只喜歡你。半分鐘之後,那妞就回了:另外兩個是誰,你為什麼只喜歡我啊?

程式猿修仙之路--演算法之直接插入排序

新增關注,檢視更精美版本,收穫更多精彩

image

相關文章