python數據結構與算法--排序

1、常用排序算法

排序算法 平均時間 最差情形 穩定度 備注
快速排序 O(nlogn) O(n^2) 不穩定 n大時較好
冒泡排序 O(n^2) O(n^2) 穩定 n小時較好
選擇排序 O(n^2) O(n^2) 不穩定 n小時較好
插入排序 O(n^2) O(n^2) 穩定 大部分已排好序時較好
歸并排序 O(nlogn) O(nlogn) 穩定 n大時較好
希爾排序 O(nlogn) O(n^s) 1<s<2 不穩定 s是所選分組

2、快速排序法

  • 基本思想:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然后再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。注:快速排序不是一種穩定的排序算法,也就是說,多個相同的值的相對位置也許會在算法結束時產生變動。

  • 一趟快速排序算法(首先任意選取一個數據(通常選用數組的第一個數)作為關鍵數據,然后將所有比它小的數都放到它前面,所有比它大的數都放到它后面)步驟:

    1)設置兩個變量i、j,排序開始的時候:i=0,j=N-1(列表長度最后一個元素下標);

    2)以第一個列表元素作為關鍵數據,賦值給key,即key=A[0];

    3)從j開始向前搜索,即由后開始向前搜索(j-=1),找到第一個小于key的值A[j],將A[j]和A[i]互換;

    4)從i開始向后搜索,即由前開始向后搜索(i+=1),找到第一個大于key的A[i],將A[i]和A[j]互換;

    5)重復第3、4步,直到i=j; (3,4步中,沒找到符合條件的值,即3中A[j]不小于key,4中A[i]不大于key的時候改變j、i的值,使得j=j-1,i=i+1,直至找到為止。找到符合條件的值,進行交換的時候i, j位置不變。另外,i=j這一過程一定正好是i+或j-完成的時候,此時令循環結束)。

  • 示例:

    假設用戶輸入了如下數組:

    下標 0 1 2 3 4 5
    數據 6 2 7 3 8 9

    創建變量i=0(指向第一個數據), j=5(指向最后一個數據), key=6(賦值為第一個數據的值)。

    (1)要把所有比key小的數移動到key的左面,故先開始尋找比6小的數,從j開始從右往左找,不斷遞減變量j的值(j-=1),找到第一個下標3的數據比6小,于是把數據3移到下標0的位置(A[i]=A[j]),把下標0的數據6移到下標3,完成第一次比較:

    下標 0 1 2 3 4 5
    數據 3 2 7 6 8 9

    (2)i=0,j=3,key=6,第二次比較(通俗來說:移動指針從j開始的,而因為第一個比較時,找到了值比key小,故該值由key的右邊移到了左邊,稱做發生了“交叉變換”行為,故移動指針變為i了,要去找比key大的值了;說明:之后每發生一次所謂的“交叉變換”,移動指針都要變化的),移動指針為i,從前往后找,遞加變量i,發現下標2的數據是第一個比key大的,于是用下標2的數據7和j指向的下標3的數據的6做交換,數據狀態變成下表:

    下標 0 1 2 3 4 5
    數據 3 2 6 7 8 9

    (3)i=2,j=3,key=6,移動指針變為j,故遞減變量j,不斷重復進行上面的循環比較。

    (4)直到i=j:都指向了下標2。于是,第一遍比較結束。得到結果如下,凡是key(=6)左邊的數都比它小,凡是key右邊的數都比它大:

    下標 0 1 2 3 4 5
    數據 3 2 6 7 8 9

    如果i和j沒有碰頭的話,就遞加i找大的,還沒有,就再遞減j找小的,如此反復,不斷循環。注意判斷和尋找是同時進行的。然后,對key兩邊的數據,再分組分別進行上述的過程,直到不能再分組為止。

    注意:第一遍快速排序不會直接得到最終結果,只會把比key大和比key小的數分到key的兩邊。為了得到最后結果,需要再次對下標2兩邊的數組分別執行此步驟,然后再分解數組,直到數組不能再分解為止(只有一個數據),才能得到正確結果。

  • Python代碼

    def quickSort_main(data_list,i,j):#快速排序法主調用程序,調用開始時i=1,j=len(data_kist)-1
        if i < j:
            split_point = quickSort_spec(data_list,i,j)
            quickSort_main(data_list,i,split_point) #遞歸
            quickSort_main(data_list,split_point+1,j)
    def quickSort_spec(data_list,i,j):#快速排序具體過程
        key = data_list[i]
        while i < j:
            while i < j and data_list[j]>=key:
                j-=1
            data_list[i] = data_list[j]#不在循環里了,即data_list[j]<key,要將j下標對應的值放到i位置
            while i < j and data_list[i]<=key:
                i +=1
            data_list[j] = data_list[i]#不在循環里了,即data_list[i]>key,要將i下標對應的值放到j位置
        data_list[i] = key #此時i=j,即找到了key要放的位置
        return i   #返回key所在下標位置
    
    >>>data_list = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    >>>quickSort_main(data_list,0,len(data_list)-1)
    >>>print(data_list)
    [17, 20, 26, 31, 44, 54, 55, 77, 93]
    

3、冒泡排序法

  • 基本思想

    每個回合都從第一個元素開始和它后面的元素比較,如果比它后面的元素更大的話就交換,一直重復,直到這個元素到達了所進行排序元素的最后位置。繼續重復操作,每次遍歷都會將剩下的元素中最大的那個放到了序列的“最后”(除去了前面已經排好的那些元素),即排序一次后大的元素會經由交換慢慢“浮”到數列的頂端(冒泡命名的來源)。算法時間復雜度為O(n^2)

  • 步驟

    (1)比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。

    (2)對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最后一對。在這一點,最后的元素應該會是最大的數。

    (3)針對所有的元素重復以上的步驟,除了最后一個。

    (4)持續每次對越來越少的元素重復上面的步驟,直到沒有任何一對數字需要比較。

  • 示例

    冒泡排序

    上圖為一輪排序,可見一輪排序后,最大的元素93會"浮"到頂端,下一輪排序則對(26,54,17,77,31,44,55,20)進行,以此重復,直到沒有元素

  • Python代碼

    def bubble_sort(data_list):#冒泡排序法
        length = len(data_list)
        exchanges = True
        while length > 0 and exchanges:
            exchanges = False
            for i in range(length-1):
                if data_list[i]>data_list[i+1]:
                    exchanges = True
                    #python中可直接交換兩個變量,而不需引入臨時變量temp來交換
                    data_list[i],data_list[i+1] = data_list[i+1],data_list[i] 
            length-=1 #一輪排序后,已放置好的那個元素不需要再參與下一輪的排序
    
    >>>data_list = [5, 3, 7, 9, 6, 1, 10]
    >>>bubble_sort(data_list)
    >>>print(data_list)
    [1, 3, 5, 6, 7, 9, 10]
    #增加exchanges作為標識,即如果有一趟排序中沒有產生交換的話,那么說明此刻數列以及變成了有序的數列,如:5,1,2,3,4,冒泡一次之后就變成了:1,2,3,4,5,已經有序了,我們沒有必要再去比較了。
    

4、選擇排序法

  • 基本思想

    每個回合都選擇出剩下的元素中最小(大)的那個,選擇的方法是首先默認第一元素是最小(大)的,如果后面的元素比它小(大)的話,那就更新當前的最小(大)的元素值,直到找到最后,把當前參與排序中的第一個元素和找到的那個最小(值)所在的位置交換。以此重復排序,直到排序完成。時間復雜度O(n^2),選擇排序法是不穩定排序算法

  • 步驟

    (1)在待排序記錄A[1]~A[n]中選出最小的記錄,將它與A[1]交換

    (2)在待排序記錄A[2]~A[n]中選出最小的記錄,將它與A[2]交換

    (3)以此類推,第i趟在待排序記錄A[i]~A[n]中選出最小的記錄,將它與A[i]交換,使有序序列不斷增長直到全部排序完畢。

  • 示例

    初始序列:{49 27 65 97 76 12 38} ,其中大括號內為無序區,大括號外為有序序列

    第1趟:先假設第一個元素49是最小的,然后不停往后比較,找到12是當前最小的元素,則將12與49交換:12{27 65 97 76 49 38}

    第2趟:在剩余元素中假設27最小的,不停查找到最后,27還是最小的,故保持27不動 :12 27{65 97 76 49 38}

    第3趟:方法同上,此處是65與38交換:12 27 38{97 76 49 65}

    第4趟:方法同上,此處是97與49交換:12 27 38 49{76 97 65}

    第5趟:方法同上,此處是76與65交換:12 27 38 49 65{97 76}

    第6趟:方法同上,此處是97與76交換:12 27 38 49 65 76 97 完成

  • Python代碼

    def select_sort(data_list):#選擇排序法
        length = len(data_list)    
        for i in range(length): 
            min_index = i #記錄最小元素的下標(每次都是把參與排序的第一個元素先作為最小值)
            for j in range(i+1,length): #在i+1開始一直往后查找最小值
                if data_list[j] < data_list[min_index]:  
                    min_index = j #找到了更小的值,故當前最小元素下標變為了j
            if min_index != i: #min_index=i時,是不用交換的 
                data_list[i],data_list[min_index] = data_list[min_index],data_list[i]
                #每次交換,都是把找到的最小值放在參與排序元素里的第一位
    >>>data_list = [5, 3, 7, 9, 6, 1, 10]
    >>>select_sort(data_list)
    >>>print(data_list)
    [1, 3, 5, 6, 7, 9, 10]
    

5、插入排序法

  • 基本思想

    輸入一個元素,檢查數組列表中的每個元素,將其插入到一個已經排好序的數列中的適當位置,使數列依然有序,當最后一個元素放入合適位置時,該數組排序完畢。

  • 示例

    假設用戶輸入序列為[7,3,1,4,8] (目標:判斷數字有沒有在正確的位置上,做法:與左邊的數字對比)

    (1)首先從第二個數字3開始,看看3有沒在正確的位置上,與7對比,比7小,故交換位置,此時序列變為

    [3,7,1,4,8]

    (2)看看第三個數字1是否在正確的位置上,與7比較,比7小,故與7交換,再與左邊的3比較,比3小,再次與3交換,此時序列變為[1,3,7,4,8]

    (3)看看第四個數字4是否在正確的位置上,與7比較,比7小,故與7交換,再與左邊的3比較,比3大,不用交換了,此時序列變為[1,3,4,7,8]

    (3)看看第五個數字8是否在正確的位置上,與7比較,比7大,不用交換了,元素遍歷完畢,排序完成

  • Python代碼

    def insert_sort(data_list):#插入排序法
        length = len(data_list)
        for i in range(1,length): #從第二個元素開始尋找其“正確”位置
            key = data_list[i] #設置當前元素值為key
            j = i - 1 #與當前值key的前一個元素對比        
            while j >= 0 and data_list[j] > key:
              data_list[j+1] = data_list[j] #前一個元素大于當前值key,則將前一個元素后移一位
              j-=1
            data_list[j+1] = key #j+1即為當前值key的合適位置
        return data_list
    
    >>>data_list = [5, 3, 7, 9, 6, 1, 10]
    >>>print(insert_sort(data_list))
    [1, 3, 5, 6, 7, 9, 10]
    

6、歸并排序法(合并排序法)

  • 基本思想

    典型的是二路合并排序,將原始數據集分成兩部分(不一定能夠均分),分別對它們進行排序,然后將排序后的子數據集進行合并,這是典型的分治法策略(分治思想是將每個問題分解成個個小問題,將每個小問題解決,然后合并)。時間復雜度O(nlogn)

  • 步驟

    (1):將n個元素分成兩個含n/2元素的子序列

    (2):用歸并排序法將兩個子序列遞歸排序(最后可以將整個原序列分解成n個子序列)

    (3):合并兩個已排序好的子序列(合并的過程是將兩個子序列排序,合成一個有序的序列)

  • 示例

    假設輸入序列為[54, 26, 93, 17, 77, 31, 44, 55, 20]

    (1)首先將序列拆分成二等分:[54,26,93,17]和[77, 31, 44, 55, 20]

    (2)對左邊的序列[54,26,93,17]繼續執行拆分,分為[54,26]和[93,17],再遞歸拆分,分為[54]和[26],此時序列中均只剩一個元素了,則進行合并比較操作,54>26,result=[26,54]

    (3)再對右邊序列[93,17]拆分,分為[93]和[17],序列中均只剩一個元素了,故進行合并比較操作,93>17,result=[26,54]

    (4)對(2)(3)步得到的序列[26,54]和[17,93]遞歸合并排序(在此講述一次合并步驟,其他不再贅述),首先i=0,j=0,26>17,故在result=[ ]中加入17,此時移動指針變為j,故j+=1;再繼續比較,26<93,在result中加入26,則result=[17,26],這時移動指針變為i了,故i+=1;再繼續比較54和93,54<93,在result中加入54,則result=[17,26,54];此時左邊序列已遍歷完,故可直接將右邊序列元素加到result后面了,即result += right[j:],得到result=[17,26,54,93]

    (5){77, 31, 44, 55, 20}操作也同理,得到[20,31,44,55,77]

    (6)最后對[17,26,54,93]與[20,31,44,55,77]進行合并排序即可

    歸并排序

    ?

    歸并排序2
  • Python代碼

    def merge_sort(data_lists):
        # 歸并排序
        if len(data_lists) <= 1:#只有一個元素時,不進行切分,而是返回列表,緊接著開始第一次merge
            return data_lists
        num = int(len(data_lists) / 2)#每次都是二等分
        left = merge_sort(data_lists[:num])#遞歸執行:拆分成二等份步驟
        right = merge_sort(data_lists[num:])
        return merge(left, right)
    def merge(left, right):
        i, j = 0, 0
        result = []
        while i < len(left) and j < len(right):
            if left[i] < right[j]:
                result.append(left[i])
                i += 1
            else:
                result.append(right[j])
                j += 1
        #退出循環時,表left或者right有一個已經遍歷完畢。假設right遍歷完畢,則是將剩余的left里的值全部添加到result中,此時right[j:]為[]的
        result += left[i:] 
        result += right[j:] 
        return result
    
    >>>a_list = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    >>>print(merge_sort(a_list))
    [17, 20, 26, 31, 44, 54, 55, 77, 93]
    

7、希爾排序法

  • 基本思想

    是插入排序的一種。也稱縮小增量排序,是直接插入排序算法的一種更高效的改進版本。其先取一個小于n的整數d1作為第一個增量,把文件的全部記錄分組。所有距離為d1的倍數的記錄放在同一個組中。先在各組內進行直接插入排序;然后,取第二個增量d2<d1重復上述的分組和排序,直至所取的增量dt=1(dt<dt-1<...<d2<d1)即所有記錄放在同一組中進行直接插入排序為止。(實質是分組插入

  • 示例

    假設用戶輸入[49,38,65,97,76,13,27,49,55,04],

    (1)第一趟初始增量d1=10/2=5,即將10個待排記錄增量分為5個子序列,分別為[49,13],[38,27],[65,49],[97,55],[76,04]

    (2)對上述5個分組分別進行插入排序(具體插入排序步驟看上面講述),每次排序時插回原序列對應的位置中,如[49,13],開始時49下標為0,13下標為5,而13<39,故排序后,下標0的位置是13,而49移動到原來13所在的下標位置5,其他同理,第一趟排序后序列變為[13,27,49,55,04,49,38,65,97,76]

    (3)縮小增量d2=5/2=2,即將第一趟排序后的序列分為2組,分別為[13,49,04,38,97],[27,55,49,65,76]

    (4)對上述2個分組分別進行插入排序(具體插入排序步驟看上面講述),第2趟排序后序列變為[04,27,13,49,38,55,49,65,97,76]

    (5)縮小增量d3=2/2=1,即將第二趟排序后的序列分為1組,[04,27,13,49,38,55,49,65,97,76]

    (6)對上述分組[04,27,13,49,38,55,49,65,97,76]進行插入排序,第3趟排序后序列變為[04,13,27,38,49,49,55,65,76,97]

    希爾排序
  • Python代碼

    def shell_sort(a_list):#希爾排序
        sublist_count = len(a_list) // 2 #增量因子d
        while sublist_count > 0:  
            for start_position in range(sublist_count):
                gap_insertion_sort(a_list, start_position, sublist_count) #按照d分組后得到的值進行插入排序
            print("增量因子為:", sublist_count, " 排序后列表為:", a_list)
            sublist_count = sublist_count // 2 #增量因子d=1時,完成所有排序
    
    def gap_insertion_sort(a_list, start, gap):#插入排序法的步驟
        for i in range(start + gap, len(a_list), gap): #從第二個增量后的值start+gap開始進行插入排序
            current_value = a_list[i]
            position = i
            while position >= gap and a_list[position - gap] > current_value:
                a_list[position] = a_list[position - gap]
                position = position - gap
                a_list[position] = current_value
    
    >>>a_list = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    >>>shell_sort(a_list)
    print(a_list)
    增量因子為: 4  排序后列表為: [20, 26, 44, 17, 54, 31, 93, 55, 77]
    增量因子為: 2  排序后列表為: [20, 17, 44, 26, 54, 31, 77, 55, 93]
    增量因子為: 1  排序后列表為: [17, 20, 26, 31, 44, 54, 55, 77, 93]
    [17, 20, 26, 31, 44, 54, 55, 77, 93]
    
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 概述 排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部...
    蟻前閱讀 5,215評論 0 52
  • 概述:排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部...
    每天刷兩次牙閱讀 3,742評論 0 15
  • 查找和排序都是程序設計中經常用到的算法。查找相對而言較為簡單,不外乎順序查找、二分查找、哈希表查找和二叉排序樹查找...
    eagleRock閱讀 5,612評論 0 14
  • 背景 一年多以前我在知乎上答了有關LeetCode的問題, 分享了一些自己做題目的經驗。 張土汪:刷leetcod...
    土汪閱讀 12,768評論 0 33
  • 赴一場盛宴 舉杯同飲歡 倏爾醉于桌 燈光漸迷離 舉足無定數 喧鬧繞耳渦 觥籌交錯后 各自卷離席
    莫名蓬特人閱讀 278評論 0 1