排序演算法Python(冒泡、選擇、快速、插入、希爾、歸併排序)

caodongfang126發表於2018-01-17

排序演算法Python(冒泡、選擇、快速、插入、希爾、歸併排序)

排序有內部排序和外部排序,內部排序是資料記錄在記憶體中進行排序,而外部排序是因排序的資料很大,一次不能容納全部的排序記錄,在排序過程中需要訪問外存。
我們通常所說的排序演算法往往指的是內部排序演算法,即資料記錄在記憶體中進行排序。

內部排序的分類:

  • 一種是比較排序,時間複雜度O(nlogn) ~ O(n^2),主要有:氣泡排序,選擇排序,快速排序,插入排序,希爾排序,歸併排序,堆排序等。

  • 另一種是非比較排序,時間複雜度可以達到O(n),主要有:計數排序,基數排序,桶排序等。

常見排序演算法的一些特性:

這裡寫圖片描述

氣泡排序

這裡寫圖片描述

通過上面的動圖也可以看出來,冒泡通過兩重迴圈遍歷每一個數後將最大的’冒’出去
冒泡是相鄰元素之間的比較,每次把最大的’冒’出去

由於需要經過兩重迴圈,所以時間複雜度為:O(n2)

選擇排序

這裡寫圖片描述
選擇排序相比氣泡排序不穩定,時間複雜度也是。
選擇排序沒趟都會產生最小值,它不是相鄰元素的比較而是在該元素設定一個索引i
然後與陣列的其他元素依次比較(除了上一個索引值),直到找到小於該元素(索引j)時交換兩元素,
接著繼續從i索引(此時已經不是原來的數值)值與索引j+1值比較。重複上述比較過程:

冒泡是相鄰元素比較,選擇不是相鄰元素比較

選擇排序是把最小的選出來

快速排序

這裡寫圖片描述

(1) 從數列中挑出一個基準值。
(2) 將所有比基準值小的擺放在基準前面,所有比基準值大的擺在基準的後面(相同的數可以到任一邊);在這個分割槽退出之後,該基準就處於最終它應該在的地方。
(3) 遞迴地把”基準值前面的子數列”和”基準值後面的子數列”進行排序。

快速排序的時間複雜度在最壞情況下是O(N2),平均的時間複雜度是O(N*lgN)。

假設有如下陣列,將兩個哨兵設在左右端,最左端的值為基準
1.右邊向左運動,直到找到一個比基準小的數
2.左邊向右運動,直到找到一個比基準大的數
這裡寫圖片描述
3.交換兩個數
這裡寫圖片描述
這裡寫圖片描述
4.如果兩個哨兵不相遇,則繼續上述步驟
這裡寫圖片描述
5.相遇之後和基準交換
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

這樣‘6’就永遠在它最終應該待的地方了,對6的前一半和後一半進行上述完整操作即可(遞迴)

參考文獻:
http://developer.51cto.com/art/201403/430986.htm

插入排序

這裡寫圖片描述

(1) 初始時,a[0]自成1個有序區,無序區為a[1..n-1]。令i=1
(2) 將a[i]併入當前的有序區a[0…i-1]中形成a[0…i]的有序區間。
(3) i++並重復第二步直到i==n-1。排序完成。

直接插入排序的時間複雜度是O(n2)

希爾排序

這裡寫圖片描述

是插入排序的一種更高效的改進版本。希爾排序是非穩定排序演算法。分組的插入排序

先將整個待排元素序列分割成若干個子序列(由相隔某個“增量”的元素組成的)分別進行直接插入排序,然後依次縮減增量再進行排序,待整個序列中的元素基本有序(增量足夠小)時,再對全體元素進行一次直接插入排序。因為直接插入排序在元素基本有序的情況下(接近最好情況),效率是很高的,因此希爾排序在時間效率上比前兩種方法有較大提高。

如排序資料為[ 5 9 7 2 3 1 6 ],那麼資料長度為7

第一次分割gap=7/2=3
i和j之間總是間隔gap個單位,其中j從gap遍歷到資料中的最後一個數,j=i-gap
這裡寫圖片描述
如上圖,最後一次交換時jgap>=0,但在這裡沒有什麼影響,下面將看出使用jgap的作用

第二次分割gap=3/2=1
這裡寫圖片描述
當分割的間隔是1的時候,首先交換前兩個的值,當j和i分別移到3和1的時候,首先交換,因為3>1,然後應該往前迭代,即j=gap,否則的話我們只比較了3和1,而實際上最前面的2也是大於1的,也應該交換。

如此往復,我們就會得到最終的排序結果。

歸併排序

這裡寫圖片描述
這裡寫圖片描述

是分治法的一種,上圖可以清晰的描述排序過程

先拆分(遞迴),後合併

效率為 O(n log n)

Python的實現

'''
氣泡排序

重複走訪過要排序的序列,一次比較兩個元素,如果他們的順序錯誤就將他們進行交換,一次冒上來的是最小的,其次是第二小。

時間複雜度:O(n^2)

空間複雜度:O(1)

穩定性:穩定
'''
def BubbleSort(data):
    for i in range(len(data)):
        for j in range(len(data)-i-1):
            if data[j]>data[j+1]:
                data[j+1] , data[j] = data[j] , data[j+1]

'''
選擇排序

選擇排序相比氣泡排序不穩定,時間複雜度也是。選擇排序沒趟都會產生最小值,它不是相鄰元素的比較而是在該元素設定一個索引i。
然後與陣列的其他元素依次比較(除了上一個索引值),直到找到小於該元素(索引j)時交換兩元素,
接著繼續從i索引(此時已經不是原來的數值)值與索引j+1值比較。重複上述比較過程……簡單的原理圖如下:

冒泡是相鄰元素比較,選擇不是相鄰元素比較
'''
def SelectionSort(data):
    for i in range(len(data)):
        for j in range(i+1,len(data)):
            if data[j]<data[i]:
                data[i] , data[j] = data[j] , data[i]

'''
快速排序

快速排序流程:
(1) 從數列中挑出一個基準值。
(2) 將所有比基準值小的擺放在基準前面,所有比基準值大的擺在基準的後面(相同的數可以到任一邊);在這個分割槽退出之後,該基準就處於最終它應該在的地方。
(3) 遞迴地把"基準值前面的子數列"和"基準值後面的子數列"進行排序。

快速排序的時間複雜度在最壞情況下是O(N2),平均的時間複雜度是O(N*lgN)。
'''               
def QuickSort(lists, left, right):
    # 快速排序
    if left >= right:
        return lists
    key =left
    low = left
    high = right
    while left < right:
        while left < right and lists[right] >= lists[key]:#如果右邊比基準小,停下
            right -= 1
        while left < right and lists[left] <= lists[key]:#如果左邊比基準大,停下
            left += 1
        lists[right],lists[left]=lists[left],lists[right]#交換現在的左右值
    lists[right] ,lists[key]=lists[key],lists[right] #left和right匯合後和基準交換

    print_data(data)#交換過程
    QuickSort(lists, low, left - 1)
    QuickSort(lists, left + 1, high)
    return lists


'''
直接插入排序

1. 初始時,a[0]自成1個有序區,無序區為a[1..n-1]。令i=1
2. 將a[i]併入當前的有序區a[0…i-1]中形成a[0…i]的有序區間。
3. i++並重復第二步直到i==n-1。排序完成。

直接插入排序的時間複雜度是O(N2)。
假設被排序的數列中有N個數。遍歷一趟的時間複雜度是O(N),需要遍歷多少次呢?N-1!因此,直接插入排序的時間複雜度是O(N2)。
'''
def InsertionSort(data):
    for i in range(1,len(data)):
        key=data[i]
        j=i-1
        while j>=0:
            if data[j]>key:
                data[j+1]=data[j]
                data[j]=key
            j-=1
'''
希爾排序

是插入排序的一種更高效的改進版本。希爾排序是非穩定排序演算法。分組的插入排序

j-=gap第一次交換資料後,看它是後面的數否還小於前面的數

如2 3 1 5 9 6這個序列以1位步長的話
一次交換後2 1 3 5 9 6此時j指向第二個數,i指向第三個數 
所以交換後應該用j-gap往前檢視是否前面的更小
'''

def ShellSort(data):
    gap=int(len(data)/2) #排序的分組
    while gap>0:
        for i in range(gap,len(data)):
            j=i-gap
            while data[j]>data[i] and j >=0:
                data[j],data[i]=data[i],data[j]
                j-=gap
                i-=gap
        gap=int(gap/2)

'''
歸併排序

先拆分,後合併
'''
def MergeSort(ls):
    if len(ls)<2:
        return ls
    mid = len(ls) >> 1 #相當於除2取整
    left = MergeSort(ls[:mid])

    right = MergeSort(ls[mid:])
    return merge(left,right)


def merge(left, right):
    result = []
    i, j = 0, 0
    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
    result += left[i:]
    result += right[j:]
    return result

'''列印函式'''
def print_data(data):
    for i in data:
        print(i,end=' ')
    print() 


'''測試程式碼'''
data=[5,9,7,2,3,1,6]
BubbleSort(data)
print_data(data)
data=[5,9,7,2,3,1,6]
SelectionSort(data)
print_data(data)
data=[5,9,7,2,3,1,6]
QuickSort(data,0,6)
print_data(data)
data=[5,9,7,2,3,1,6]
InsertionSort(data)
print_data(data)
data=[5,9,7,2,3,1,6]
ShellSort(data)
print_data(data)
data=[5,9,7,2,3,1,6]
data=MergeSort(data)
print_data(data)


轉載:http://blog.csdn.net/qq_33414271/article/details/78528891    感謝博主分享

相關文章