直接插入排序的兩種作法

可能不少人不會留意到這個問題,今天剛好碰到了,而後來稍微討論一下算法

 

直接插入排序應該是不少數據結構與算法書裏第一個講的排序算法,算法的描述是這樣的:數組

把待排序列視做兩段,一段是已排序列,一段是未排序列。每一趟排序時,爲未排序列的首位在已排序列中進行查找(由於是直接插入排序,因此這裏特指逐個比較)其合適的位置,而後將其插入(插入的過程當中伴隨着一系列元素的後移)。數據結構

 

當時沒有想太多,直接寫了個按文字描述的代碼:性能

def InsertSort1(LIST):
    for i in range(1,len(LIST)):#從1開始,將0元素視爲已排序列
        TEMP=LIST[i]#保存中間變量
        for ii in range(0,i):
            if LIST[ii]>TEMP:#從前日後逐次比較,若是出現比它大的元素,那麼就說明他應該落在此位。此處未加等於號是爲了保證排序穩定性
                for iii in range(i,ii,-1):#注意,從後往前位移元素
                    LIST[iii]=LIST[iii-1]
                LIST[ii]=TEMP
                break
    return LIST

 

後來在看的時候發現好像不太對勁,怎麼嵌套了三層for,因而乎回憶起了這一段應該是從後往前進行比較的spa

def InsertSort2(LIST):
    for i in range(1,len(LIST)):
        TEMP=LIST[i]
        flag=True
        for ii in range(i-1,-1,-1):#從後往前進行逐次比較
            if LIST[ii]>TEMP:
                LIST[ii+1]=LIST[ii]
            else:
                 LIST[ii+1]=TEMP
                 flag=False
                 break
        if flag:
            LIST[0]=TEMP
    return LIST

這裏使用了兩個輔助變量,TEMP和flagcode

 

事實上不少書在寫這一段的時候,用了另外一種巧妙的方法,只是用了一個輔助變量,這裏能夠參閱其餘文章,我當時寫到這裏的時候馬上就回憶起了書裏把這個作法叫作哨兵,即數組的0元素不用來儲存元素,做爲temp使用,在須要flag的地方不須要使用flag,保持了操做的一致性。這其實並非咱們要討論的重點。blog

 

乍一看,二方法只嵌套了兩層for,是否是比一方法要好呢?排序

答案是否認的,其實這個問題用一個圖就能很好的表達了:class

 

InsertSort1() 的性能十分穩定,在圖中,它逐次比較的「路程」和後續位移的「路程」加起來剛好就是前一段線長(+1)變量

而InsertSort2()的性能不是那麼穩定,在圖中,當待排元素在已排序列中合適的位置靠後時,它的性能無疑是更好(付出了更短的「路程」),而當待排元素靠前時,它的性能就變差了(付出了雙倍,更長的「路程」)

 

其實兩個作法都是符合插入排序思想的,平均性能一致,最好性能的狀況下2好於1,最壞狀況下1>2

老實說真沒想到這個最初始的算法會藏着這麼一個有趣的小祕密,也算是很有收穫了

相關文章
相關標籤/搜索