python算法與數據結構學習筆記

排序算法

1. 冒泡排序

冒泡排序,從左到右,分別比較兩個相鄰數字的大小,如果後面一個數小於前面的數就進行交換。

def bubble_sort(seq):
    l = len(seq)
    for i in range(l-1):
        for j in range(l-1-i):
            if seq[j] > seq[j+1]:
                seq[j],seq[j+1] = seq[j+1],seq[j]
                print(seq)

輸入一個list:  A = [5, 6, 3, 4, 1, 0, 2]

結果:

可以看到一輪輪下來大的數字被慢慢往後移。

 

2. 選擇排序

每次從序列中找到最小的數插入序列的迭代位置。這樣排序結束就是從小到大。

def select_sort(seq):
    l = len(seq)
    for i in range(l-1):
        min_val =  i
        for j in range(i+1,l):
            if  seq[j]<seq[min_val]:
                min_val = j
        if min_val!= i:
            seq[min_val],seq[i] = seq[i],seq[min_val]
        print(seq)

相同的list:

結果:

 

3. 插入排序

假設從第一個元素開始,每一個元素都是新插進來的,那麼每次插進的元素和之前的比較,放入該放的地方即可。

def insert_sort(seq):
    l = len(seq)
    for i in range(1,l):
        val =  seq[i]
        pos = i
        while val < seq[pos-1] and pos > 0:
            seq[pos] =seq[pos-1]
            pos-=1
        seq[pos] = val
        print(seq)

結果:

 

4.歸併排序

基本思想就是:

  • 分解:將待排序的 n 個元素分成各包含 n/2 個元素的子序列
  • 解決:使用歸併排序遞歸排序兩個子序列
  • 合併:合併兩個已經排序的子序列以產生已排序的答案
# 用遞歸的思想來解決:
# 首先找到中間的數字作爲基準,在左右兩邊分別進行排序,最後將兩個有序的序列進行合併:
def merge_sort(seq):
    if len(seq) <= 1:
        return seq
    else:
        mid = int(len(seq)/2)
        left = merge_sort(seq[:mid])
        right = merge_sort(seq[mid:])
        new_seq = merge_sort_list(left,right)
        return new_seq

# 此函數用來合併兩個已經排序好的list
# 定義兩個指針,將左右排序好的list依次放入最後的list裏形成一個有序的最終數組
def merge_sort_list(left,right):
    new_seq = []
    a = b =0
    while a < len(left) and b < len(right):
        if left[a] < right[b]:
            new_seq.append(left[a])
            a += 1
        else:
            new_seq.append(right[b])
            b += 1
    if a < len(left):
        new_seq.extend(left[a:])
    else:
        new_seq.extend(right[b:])
    return new_seq

進行測試:

seq =[10,23,51,18,4,31,13,5] 
merge_sort(seq)
# result: [4, 5, 10, 13, 18, 23, 31, 51]

時間複雜度爲O(nlogn)

 

5. 快速排序

快速排序是比較重要以及常用的一種排序方法。基本思想爲:

  • 選擇基準值 pivot 將數組分成兩個子數組:小於基準值的元素和大於基準值的元素。這個過程稱之爲 partition
  • 對這兩個子數組進行快速排序。
  • 合併結果
def quick_sort(seq):
    if not seq or len(seq) <=1:
        return seq
    else:
        pivot_idx = 0
        l_seq = [seq[i] for i in range(len(seq)) if seq[i] <= seq[pivot_idx] and pivot_idx != i] # 特別注意要加上 pivot_idx != i,不然會出現死循環
        r_seq = [seq[i] for i in range(len(seq)) if seq[i] >  seq[pivot_idx]and pivot_idx != i]
    return(quick_sort(l_seq)+[seq[pivot_idx]] +quick_sort(r_seq))

上面的快排遍歷了數組兩次:

1. 需要額外存儲; 2. partition每次都遍歷了整個數組

我們想實現原地排序可以使用這種方法:

設置首位倆個指針 left, right,兩個指針不斷向中間收攏。如果遇到 left 位置的元素大於 pivot 並且 right 指向的元素小於 pivot,我們就交換這倆元素,當 left > right 的時候退出就行了,這樣實現了一次遍歷就完成了 partition。

def quicksort_inplace(array, beg, end):    # 左閉右開區間,end 傳入 len(array)
    if beg < end:    # beg == end 的時候遞歸出口
        pivot = partition(array, beg, end)
        quicksort_inplace(array, beg, pivot)
        quicksort_inplace(array, pivot+1, end)

# 實現partition操作:
def partition(array, beg, end):
    pivot_index = beg
    pivot = array[pivot_index]
    left = pivot_index + 1
    right = end - 1    # 開區間,最後一個元素位置是 end-1    

    while True:
        # 從左邊找到比 pivot 大的
        while left <= right and array[left] < pivot:
            left += 1

        while right >= left and array[right] >= pivot:
            right -= 1

        if left > right:
            break
        else:
            array[left], array[right] = array[right], array[left]

    array[pivot_index], array[right] = array[right], array[pivot_index]
    return right   # 新的 pivot 位置

 

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