程序猿修仙之路--算法之希爾排序


     

           


           


IT

湖          

       自馮諾依曼開啟大計算機時代以來,經過近一個世紀的蓬勃發展,已然成為一個人才眾多的群體:IT江湖            

        依附市場規律,江湖上悄然興起數十宗門,其中以AI,大數據近期最為熱門。每個宗門人才濟濟,搶奪人才大戰早已在阿里,騰訊,百度等數百個國度白熱化。            

      IT江湖人士憑借JAVA,Python等武器,在精通各路內功心法的基礎上在各個國度揚名立萬,修仙成佛者眾多,為后人樹下追寵之榜樣。            

    內功心法眾多,其中以算法最為精妙,是修仙德道必經之路。            

       
               

雖然江湖上算法內功繁多,但是好的算法小編認為必須符合以下幾個條件,方能真正提高習練者實力:

1            

時間復雜度(運行時間)        

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

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


2            

空間復雜度(額外的內存使用)        

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

3            

結果的正確性        

    這個指標是菜菜自己加上的,我始終認為一個優秀的算法最終得到的結果必須是正確的。就算一個算法擁有非常優秀的時間和空間復雜度,但是結果不正確,導致修煉者經脈逆轉,走火入魔,又有什么意義呢?
           

   

               

    算法雖然精妙,但需循序漸進修煉,並且需要一定的數學內功基礎方可徹底領悟。今日習練算法第三章:排序之希爾排序


氣運丹田 開啟修煉之路
           

心法簡介    

    上一篇我們修煉了插入排序,希爾排序(又名Shell's Sort)本質上屬於插入排序,是插入排序的一種更高效升級版本,也稱為**縮小增量排序**。同時希爾排序在時間復雜度上也是突破O(n²)的第一批算法之一。你說厲不厲害?~~            

心法基本思想    

        通過直接插入排序的修煉,我們知道直接插入排序是一種性能比較低的初級算法,對修煉者提升不是不大, 但是有一點優勢那就是對於小型數組或者部分有序的數組非常高效,希爾排序就是基於這一點優勢對直接插入排序進行了改良。換句話說直接插入排序低效的原因在於無序,無序的程度越高越低效。例如:最小的元素初始位置在數組的另一端,此元素要想到達正確位置,是需要一個一個位置前移,最終需要N-1次移動。如何改變這種狀態正是希爾排序的突破口。    

        希爾排序的思想是把數組下標按照一定的增量h分組,然后對每組進行直接插入排序。在進行排序時,如果h很大,我們就能將元素移動到很遠的地方,為實現更小的h有序創造方便。然后增量h逐漸減小(每個分組的元素量增多),直到h為1整個數組划分為一組,排序結束。    

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

           
               

   

心法復雜度  

 

    時間復雜度

  1. 最壞時間復雜度依然為O(n²),一些經過優化的增量序列如Hibbard經過復雜證明可使得最壞時間復雜度為O(n^3/2),最好情況下為O(n)屬於線性復雜度。                 

  2. 空間復雜度

    優於希爾排序本質上屬於插入排序升級版,所以空間上和直接插入排序一致為O(1),在常數級別。       

性能和特點       

  1. 希爾排序之所以高效是因為它權衡了子數組的規模和有序性。排序之初各個子數組都很短,這種情況很適合插入排序.             

對於增量h的選擇對希爾排序非常重要,直接影響其性能。其實除了h的選擇之外,h之間的數學性質也影響希爾排序的性能,比如它們的公因子等。很多論文研究了各種不同的遞增續列但都無法證明某個序列是最好的。對於某些基礎遞增的序列其實在性能上和某些復雜的序列接近,所以很多情況下我們沒有必要花大力氣在復雜序列上的研究上。         

 

適用場景

        與插入排序不同,希爾排序可以適用於大型數組,它對任意排序的數組表現良好,雖然不是最好。實驗證明,希爾排序比我們上兩章學習的選擇排序和插入排序要快的多,並且數組越大,優勢越大。目前最重要的結論是:希爾排序的運行時間達不到平方級別。

        對於中等大小的數組希爾排序的時間是在可接受范圍之內的,因為它的代碼量很小,而且需要的額外空間很小,幾乎可以忽略。對於其他更高效的其他算法,可能比希爾排序更高效,但是代碼也更復雜,性能上比希爾排序也高不了幾倍,所以在很多情況下希爾排序成為首選的算法。

 

其他      

1. 直接插入排序是穩定的,希爾排序呢?   

由於多次插入排序,我們知道一次插入排序是穩定的,不會改變相同元素的相對順序,但在不同的插入排序過程中,相同的元素可能在各自的插入排序中移動,最后其穩定性就會被打亂,所以希爾排序排序是不穩定的。    


   

   

修煉完畢 試煉一下吧
            
1                                
c#武器版本            
static void Main(string[] args)

       {

             List<int> data = new List<int>() ;

           for (int i = 0; i < 11; i++)            {                                data.Add(new Random(Guid.NewGuid().GetHashCode()).Next(1, 100));            }            //打印原始數組值            Console.WriteLine($"原始數據: {string.Join(",", data)}");            int n = data.Count;            int h = 1;            //計算初始化增量,網絡提供,據說比較好的遞增因子            while (h < n / 3)            {                h = 3 * h + 1;

           }

            Console.WriteLine($"初始化增量:{h}");

           while (h >= 1)            {                for (int i = h; i < n; i++)                {                    for (int j = i; j >=h&&data[j]<data[j-h]; j-=h)                    {                        //異或法 交換兩個變量,不用臨時變量                        data[j] = data[j] ^ data[j - 1];                        data[j - 1] = data[j] ^ data[j - 1];                        data[j] = data[j] ^ data[j - 1];                    }                }                h = h / 3;            }            //打印排序后的數組            Console.WriteLine($"排序數據: {string.Join(",", data)}");            Console.Read();        }

運行結果:

原始數據: 47,50,32,42,44,79,10,16,51,74,52

初始化增量:4

排序數據: 10,16,32,42,44,47,50,51,52,74,79

2                                

golang武器版本 


package main
import (
       "fmt" "math/rand"
) func main() {
   var data []int
   for i := 0; i < 11; i++ {
       data = append(data, rand.Intn(100))    }    fmt.Println(data)
       var n = len(data)
       var h = 1 for h < n/3 {    h = 3*h + 1 } fmt.Println(h)
       for h >= 1 {
           for i := h; i < n; i++ {
               for j := i; j >= h && data[j] < data[j-h]; j -= h {
                   data[j], data[j-h] = data[j-h], data[j] }    }    h = h / 3 } fmt.Println(data) }

運行結果:

[81 87 47 59 81 18 25 40 56 0 94]

4

[0 18 25 40 47 56 59 81 81 87 94]

此章已成 智力+10


菜鳥奮斗史出品
見證菜鳥的成長之路


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM