Python算法系列之常見的排序算法-------------------冒泡排序、選擇排序、插入排序、快速排序、希爾排序、歸併排序

 

排序算法

定義:是一種能將一串數據依照特定的順序進行排列的一種算法。

1.冒泡排序

思路:比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。以此類推,對每一對相鄰元素做相同處理,從開始第一隊到結尾的最後一對。這不做完之後最後的元素就會是最大的數。然後對所有的元素進行重複以上的步驟,每次都出去最後的一個。

冒泡排序
無序的序列:[54,26,93,17,77,31,44,55,20]
第一次比較,54>26,交換:26,54, 93,17,77,31,44,55,20
第二次比較,54<93,不處理:26,54, 93,17,77,31,44,55,20
第三次比較,93>17, 交換:26,54, 17,93, 77,31,44,55,20
第四次比較,93>77, 交換:26,54, 17, 77, 93, 31,44,55,20
第五次比較,93>31, 交換:26,54, 17, 77, 31,93, 44,55,20
第六次比較,93>44, 交換:26,54, 17, 77, 31, 44,93, 55,20
第七次比較,93>55, 交換:26,54, 17, 77, 31, 44, 55,93, 20
第八次比較,93>20, 交換:26,54, 17, 77, 31, 44, 55, 20,93
小結:1、最大的數93排在了隊列的末尾
           2、列表的長度n = 9,我們比較了n-1次
重複上面的過程:
  26, 17,54, 31, 44, 55, 20, 77,93
小結:1、比較了n-2次
           2、77放在了無序列表尾部 
繼續:
17, 26, 31, 44, 54, 20,55, 77,93    # 比較了 n-3 ,把55放在了無序列表的尾部
17, 26, 31, 44, 20, 54,55, 77,93    # 比較了 n-4 ,把54放在了無序列表的尾部
17, 26, 31, 20, 44, 54,55, 77,93    # 比較了 n-5 ,把54放在了無序列表的尾部
17, 26, 20, 31, 44, 54,55, 77,93    # 比較了 n-6 ,把31放在了無序列表的尾部
17, 20, 26, 31, 44, 54,55, 77,93    # 比較了 n-7 ,把26放在了無序列表的尾部
17, 20, 26, 31, 44, 54,55, 77,93    # 比較了 n-8 ,得到一個有序的序列
總結:
相鄰元素兩兩比較把最大值排在無序序列尾部這個過程,要循環n-1

代碼實現:

def bubbling_order(lis):
    for i in range(len(lis)-1, 0, -1):
        for j in range(i):
            if lis[j] > lis[j+1]:
                lis[j], lis[j+1] = lis[j+1], lis[j]
    return lis


listA = [32, 56, 6, 78, 44, 34, 45, 48]
print('冒泡排序:', bubbling_order(listA))

 

2.選擇排序

思路:首先在沒有排序的序列只能夠找到最大(小)元素,存放到排序序列的其實位置,再從剩餘未排序元素中繼續尋找最大(小)元素,放到已排序序列的末尾,重複以上操作,直到所有元素均以排序完成。

選擇排序
無序的序列:[54,26,93,17,77,31,44,55,20]
第一次,從無序序列中挑出一個最小值,放在有序序列的尾部:
17,54,26,93, 77,31,44,55,20
第二次,從無序序列中挑出一個最小值,放在有序序列的尾部:
17,20,54,26,93, 77,31,44,55
第三次,從無序序列中挑出一個最小值,放在有序序列的尾部:
17,20, 26,54, 93, 77,31,44,55
第四次,從無序序列中挑出一個最小值,放在有序序列的尾部:
17,20, 26, 31,54, 93, 77, 44,55
第五次,從無序序列中挑出一個最小值,放在有序序列的尾部:
17,20, 26, 31, 44,54, 93, 77, 55
第六次,從無序序列中挑出一個最小值,放在有序序列的尾部:
17,20, 26, 31, 44,54, 93, 77, 55
第七次,從無序序列中挑出一個最小值,放在有序序列的尾部:
17,20, 26, 31, 44,54, 55, 93, 77
第八次,從無序序列中挑出一個最小值,放在有序序列的尾部:
17,20, 26, 31, 44,54, 55, 77, 93

代碼實現:

# 選擇排序
def choice_order(lis):
    n = len(lis)
    for i in range(1, n):
        min_index = i-1
        for j in range(i, n):
            if lis[j] < lis[min_index]:
                min_index = j
        if min_index != i-1:
            lis[i-1], lis[min_index] = lis[min_index], lis[i-1]
    return lis


listB = [34, 56, 89, 7, 43, 23, 73, 21]
print('選擇排序:', choice_order(listB))

 

3.插入排序

插入排序是一種簡單直觀的排序算法

思路:通過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相對應位置並插入。

 

插入排序
無序的序列:[54,26,93,17,77,31,44,55,20]
第一次插入:[20,54,26,93,17,77,31,44,55]
第二次插入:[20,54,55,26,93,17,77,31,44]
第三次插入:[20,44,54,55,26,93,17,77,31]
第四次插入:[20,31,44,54,55,26,93,17,77]
第五次插入:[20,31,44,54,55,77,26,93,17]
第六次插入:[17,20,31,44,54,55,77,26,93]
第七次插入:[17,20,31,44,54,55,77, 93,26]
第八次插入:[17,20,26,31,44,54,55,77, 93]
總結:
待排序序列元素數 : n
插入的次數: n-1

代碼實現:

# 插入排序
def insert_order(lis):
    n = len(lis)
    for i in range(1, n):
        for j in range(i, 0, -1):
            if lis[j] < lis[j-1]:
                lis[j], lis[j-1] = lis[j-1], lis[j]
                # print(lis)
    return lis


listC = [12, 45, 9, 39, 18, 89, 48, 59]
print('插入排序:', insert_order(listC))

4.快速排序

思路:快速排序又稱爲劃分交換排序,將要排序的數據分割成獨立的兩部分,其中一部分的所有數據逗比另外一部分所有數據都要小,然後在按照此方法對兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。

 

快速排序
無序的序列:[54,26,93,17,77,31,44,55,20]
第一次快速排序,那第一個元素54作爲基準值,進行分割:
[26,17,31,44,20,54, 93,77,55]
經過第一次分割排序,基準值左邊的序列的所有元素都小於基準右邊序列的所有元素
對54 左邊的序列進行遞歸的分割:
拿26作爲基準值,進行分割:
[17,20,26, 31,44,54, 93,77,55]
對26 左邊的序列進行遞歸的分割:
拿17作爲基準值,進行分割:
[17,20,26, 31,44,54, 93,77,55]
對17 右邊的序列進行遞歸的分割:
右邊序列只有一個元素20,不能在分割,返回

對26 右邊的序列進行遞歸的分割:
拿31作爲基準值,進行分割:
[17,20,26, 31,44,54, 93,77,55]
31小於44,不處理

對54 右邊的序列進行遞歸的分割:
拿93作爲基準值,進行分割:
[17,20,26, 31,44,54, 77, 55,93]
對93 右邊的序列進行遞歸的分割:
拿77作爲基準值,進行分割:
[17,20,26, 31,44,54, 55, 77,93]
接下來,把整個序列進行排序:
[17,20,26, 31,44,54, 55, 77,93]

代碼實現:

# 快速排序
def quick_order(lis, start, end):
    # 判斷low是否小於high,如果爲false直接返回
    if start >= end:
        return lis
    mid = lis[start]   # 設置基準值
    low = start
    high = end
    while low < high:
        # 如果列表後面的數比基準值大或者相等,則前移以爲直到有比基準值小點的數出現
        while low < high and lis[high] >= mid:
            high -= 1
        # 找到就把索引[high]的值賦給第[low]個元素
        lis[low] = lis[high]
        # 基準值列表之前的做同樣處理
        while low < high and lis[low] < mid:
            low += 1
        lis[high] = lis[low]
    lis[low] = mid
    quick_order(lis, start, low-1)
    quick_order(lis, low+1, end)
    return lis


listD = [23, 56, 5, 67, 81, 21, 41, 14]
print('快速排序:', quick_order(listD, 0, len(listD) - 1))

5.希爾排序

希爾排序是插入排序的一種。也稱縮小增量排序,是直接插入排序算法的一種更高效的改進版本。

思路:希爾排序是把記錄按下標的一定增量分組,對每組使用直接插入排序算法排序,隨着增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個文件恰被分成一組,算法便終止。

代碼實現:

# 希爾排序
def shell_order(lis):
    n = len(lis)
    gap = n//2  # 設置初始步長
    while gap > 0:
        # 按照步長進行插入排序
        for i in range(gap, n):
            j = i
            # 進行插入排序
            while j >= gap and lis[j-gap] > lis[j]:
                lis[j-gap], lis[j] = lis[j], lis[j-gap]
                j -= gap
        gap = gap//2  # 設置新的步長 進行循環下一次
    return lis


listE = [68, 34, 56, 7, 23, 75, 90, 54]
print('希爾排序:', shell_order(listE))

6.並歸排序

思路:

假如我們有一個n個數的數列,下標從0到n-1
首先是拆分的過程
1 、我們按照 n//2 把這個數列分成兩個小的數列
2 、把兩個小數列 再按照新長度的一半 把每個小數列都分成兩個更小的
3、一直這樣重複,一直到每一個數列包含單個元素

之後是合併排序的過程:
3、按照最後分開的兩個數比較大小並合併成有序的數組
4 、對每組數據按照上次分開的結果,進行排序並合併成更大的數組
5、最後的到一個有序的數組

歸併排序

無序的序列:[54,26,93,17,77,31,44,55,20]

拆分過程:

第一次拆分,n = 9   n//2 = 4

拆分結果:[54,26,93,17]和[77,31,44,55,20]

遞歸拆分[54,26,93,17]的部分:

n = 5   n//2 = 2

拆分結果:[54,26]和[93,17]

遞歸拆分[54,26]的部分:

n = 2   n//2 = 1

拆分結果:[54]和[26]

合併[54]和[26],合併的結果 [26,54]

遞歸拆分[93,17]的部分:

n = 2   n//2 = 1

拆分結果:[93]和[17]

合併[93]和[17],合併的結果[17, 93]

合併left=[26,54]和right=[17, 93]

創建results =[]

定義兩個指針 lp=0,rp=0

比較left[lp]和right[rp] , 26>17

把17添加到results中,result.append(right[rp])

results=[17]

rp+=1   rp = 1

比較left[lp]和right[rp] , 26<93

把26添加到results中,result.append(left[lp])

results=[17,26]

lp+=1   lp =1

比較left[lp]和right[rp] , 54<93

把54添加到results中,result.append(left[lp])

results=[17,26,54]

lp+=1 lp=2

把93添加到results中,result.append(right[rp])

results=[17,26,54, 93]

返回results

遞歸拆分[77,31,44,55,20]的部分:

同樣的道理,遞歸拆分合並,

得到結果: [20,31, 44, 55,77]

合併left=[17,26,54, 93]和right=[20,31, 44, 55,77]

定義results = []

定義lp=0和rp =0

比較left[lp]和right[rp] , 17<20

把17添加到results中,result.append(left[lp])

results=[17]

lp+=1  lp = 1

比較left[lp]和right[rp] , 26>20

把20添加到results中,result.append(right[rp])

results=[17,20]

rp+=1

比較left[lp]和right[rp] , 26<31

把26添加到results中,result.append(left[lp])

results=[17,20,26]

lp+=1  lp = 2

。。。。。

最終結果:

[17, 20,26, 31, 44, 54, 55,77, 93]

代碼實現:

def merger_order(lis):
    if len(lis) <= 1:
        return lis
    else:
        num = len(lis)//2
        left_part = merger_order(lis[:num])
        right_part = merger_order(lis[num:])
        return merger(left_part, right_part)


def merger(left, right):
    h, r = 0, 0
    result = []
    while h < len(left) and r < len(right):
        if left[h] < right[r]:
            result.append(left[h])
            h += 1
        else:
            result.append(right[r])
            r += 1
        result += left[h:]
        result += right[r:]
        return result


listF = [29, 4, 44, 52, 21, 42, 78, 25]
print('歸併排序:', shell_order(listF))

 

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