綜合課:二步驟掌握快速排序Quicksort【初、高中信息技術】

目錄

1、二步掌握快速排序算法quicksort

1.1 步驟一:算法速覽

1.2 步驟二:算例分析

2、quicksort算法的非遞歸實現:循環+隊列


1、二步掌握快速排序算法quicksort

昨天(注:實際上這是去年寫的)準備開始看《編寫高質量代碼 改善python程序的91個建議》,才翻幾頁,就看到一個用遞歸實現的排序算法,頓時讓我眼前一亮。

def quicksort(arr): #快速排序,目標是非遞減序列
    less=[];greater=[] #左子表、右子表
    if(len(arr)<=1): #某個子表長度小於等於1時,即要麼空,要麼一個數據元素,返回子表本身
        return arr
    pivot=arr.pop() #定義pivot
    for x in arr:
        if(x<=pivot):  #比pivot小的元素都加入左子表
            less.append(x)
        else:
            greater.append(x) #比pivot大的元素都加入右子表
return quicksort(less)+[pivot]+quicksort(greater) #確定pivot位置並對兩子錶快速排序

1.1 步驟一:算法速覽

閱讀程序或讀懂算法其實和學習語文、英語類似,先一條語句一條語句地理解,然後從整體上理解:每調用一次quicksort,只確定一個數據元素pivot的位置,並分割子表;對子錶快速排序,直到子表爲空或只有一個數據元素;每返回一次,即意味着一個子表排序的完成。

1.2 步驟二:算例分析

不妨以arr=[1,4,6,3,8]爲例來分析其排序過程,看[1,3,4,6,8]是如何形成的。

根據步驟一的算法速覽,可以對quicksort的調用與數據處理過程繪製出上圖。

這裏以第一次調用quicksort來稍作解釋:要處理的子表是[1,4,6,3,8],確定了數字“8”所在位置,並分割成左子表[1,4,6,3]和右子表[](即空子表),對這兩個子表進一步作快速排序處理。後面的圖形示意依此類推。

分析上圖可知(這裏關注從下一層返回的子表中的數據,非每次調用quicksort時確定的數據,且已省略返回層節點):

第1次返回數據:1

第2次返回數據:4

第3次返回數據:(空)

第4次返回數據:4,6

第5次返回數據:1,3,4,6

第6次返回數據:(空)

第7次返回數據:1,3,4,6,8

除分析外,還可以直接跟蹤數據處理過程:

def quicksort(arr):
    global seq
    less=[];greater=[]
    if(len(arr)<=1):
        seq+=1
        print("第%d次返回數據:"%seq,end="")
        if(len(arr)==0):print("(空)")
        else: print(arr)
        return arr
    pivot=arr.pop()
    for x in arr:
        if(x<=pivot):
            less.append(x)
        else:
            greater.append(x)
    tr=quicksort(less)+[pivot]+quicksort(greater)
    seq+=1 
    print("第%d次返回數據:"%seq,end="")
    print(tr)
    return tr


if(__name__=="__main__"):
    seq=0
    arr1=arr2=[1,4,6,3,8]
    arr1=quicksort(arr1)
    print(arr1)

2、quicksort算法的非遞歸實現:循環+隊列

遞歸是一種特殊的子程序調用方式,即在子程序的實現代碼中出現對自己的調用,可視爲一種程序結構。

一句話概括,遞歸其實就是藉助系統對子程序的調用機制來迭代處理問題規模不斷縮小的數據集而解決問題。

遞歸是一種非常有意思的運行結構,和順序結構、選擇結構、循環結構一樣,只是沒有那麼必須。

那麼,如何把遞歸這種程序結構轉化成非遞歸的程序結構呢?答案是:循環+隊列。

def quicksort_nonR(L):
    IO=[];IO.append([0,len(L)-1]) #IO爲邏輯隊列,每個元素爲一個邏輯子表,初始時爲整個L
    while(len(IO)!=0):
        l=[];g=[]
        i,j=IO.pop(0) #出隊,處理一個子表
        if(i>=j):continue #無效子表,忽略
        p=L[j]  #p即quicksort中的pivot
        for n in range(i,j):
            if(L[n]<=p): #構造左子表
                l.append(L[n])
            else: #構造右子表
                g.append(L[n])
        L[i:j+1]=l+[p]+g  #每次for循環將確定某個子表中數據p的位置
        IO.append([i,i+len(l)-1])   #左子表入隊
        IO.append([i+len(l)+1,i+len(l)+len(g)])  #右子表入隊
return L

系統的函數調用藉助棧完成,因而可通過循環加與棧工作機制一定相似的隊列來實現遞歸向非遞歸的轉化。

if(__name__=="__main__"):
    arr1=[1,4,6,3,8]
    arr1=quicksort(arr1)
    arr2=[1,4,6,3,8,0,1,-23]
    arr2=quicksort_nonR(arr2)
    print(arr1)
    print(arr2)

遞歸轉化成非遞歸的一般方法與原理,大家get到了嗎?

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章