排序算法---冒泡排序, 選擇排序與插入排序 (python版)

     排序算法是面試時常考的基礎知識,今天對三個基本排序算法進行總結。

---------------------------------------------------------------------------------------------------------------

首先寫一個計算排序算法時間的裝飾器以及3個輔助函數來幫助測試排序算法性能。

裝飾器:

def timmer(func):  #func 爲傳進來的排序函數對象
    def warpper(arr, nums):  # arr:待排序數組, nums:數組長度
        start = time.time()
        r = func(arr, nums)
        end = time.time()
        print('toatl time is :', str(end - start))
        return r
    return warpper

生成無序數組或基本有序數組:

def random_list(num, start, end):  # 生成無序數組
    res = []
    for i in range(num):
        res.append(random.randint(start, end))
    return res

def list_near_order(nums, start):  #生成基本有序數組
    a = [i for i in range(start, start + nums)]
    for i in range(nums//10):
        ind1 = random.randint(0,nums)
        ind2 = random.randint(0, nums)
        a[ind1], a[ind2] = a[ind2], a[ind1]
    return a

檢查排序是否正確:

def check_list(arr,ori_arr):   #arr:排序後的數組, ori_arr: 原始數組
    new_arr = sorted(ori_arr)
    if arr == new_arr:
        return True
    return False

------------------------------------------------------------------------------------------------------

 

一、冒泡排序

基本思想:比較相鄰的兩個數,如果前者比後者大,則進行交換。每一輪排序結束,選出一個未排序中最大的數放到數組後面

冒泡排序算法的運作如下:

  1. 比較相鄰的元素,如果前一個比後一個大,就把它們兩個調換位置。
  2. 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。這步做完後,最後的元素會是最大的數。
  3. 針對所有的元素重複以上的步驟,除了最後一個。
  4. 持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。
def bubble_sort(arr):
    n = len(arr)
    for i in range(n-1):
        for j in range(1,n-i):
            if arr[j] < arr[j-1]:
                arr[j],arr[j-1] =  arr[j-1], arr[j]

特點總結

最差時間複雜度爲O(n^2),平均時間複雜度爲O(n^2)。穩定性:穩定。輔助空間O(1)。

算法改進

 n個元素比較n-1趟,第i趟比較n-i次

若在其中的某一趟排序中:若始終未發生元素的交換說明已經排序號好,函數結束!

def bubble_sort_pro(arr):
    n = len(arr)
    for i in range(n-1):
        flag = 0
        for j in range(1,n-i):
            if arr[j] < arr[j-1]:
                arr[j],arr[j-1] =  arr[j-1], arr[j]
                flag = 1
        if flag == 0:
            break

冒泡排序算法應該是我們學習的第一個排序算法,非常簡單,但是時間複雜度高,基本不會選擇排序算法來解決排序問題。

二、選擇排序

基本思想:依次選出數組最小的數放到數組的前面。首先從數組的第一個元素開始往後遍歷,找出最小的數放到第一個位置。再從剩下數組中找出最小的數放到第二個位置。以此類推,直到數組有序。

def slect_sort(arr, nums):   #arr:待排序數組, nums:數組長度
    for i in range(nums):
        minInd = i
        for j in range(i, nums):
            if arr[j] < arr[minInd]:
                minInd = j
        arr[i], arr[minInd] = arr[minInd], arr[i]

  算法特點: 

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

  選擇排序是不穩定的排序算法,不穩定發生在最小元素與A[i]交換的時刻。

  比如序列:{ 5, 8, 52, 9 },一次選擇的最小元素是2,然後把2和第一個5進行交換,從而改變了兩個元素5的相對次序。

三、插入排序

基本思想 :在要排序的一組數中,假設前面(n-1)[n>=2] 個數已經是排好順序的,現在要把第n個數找到相應位置並插入,使得這n個數也是排好順序的。如此反覆循環,直到全部排好順序。

def insert_sort(arr,nums):   #arr:待排序數組, nums: 數組長度
    for i in range(1, nums):
        j = i - 1    # i 指向的元素爲待插入的元素, j 指向的元素爲已排好序序列的最後一個
        if arr[i] < arr[j]:    #若arr[i] > arr[j] 則 i 及之前的元素均爲有序,直接過, 否則找到 i 指向的元素應該在的位置
            temp = arr[i]
            while j >= 0 and arr[j] > temp:
                arr[j+1] = arr[j]
                j -= 1
            arr[j+1] = temp

算法分析:

 

在第一趟排序中,插入排序最多比較一次,第二趟最多比較兩次,依次類推,最後一趟最多比較N-1次。因此有:

1+2+3+...+N-1 =N*(N-1)/2

因爲在每趟排序發現插入點之前,平均來說,只有全體數據項的一半進行比較,我們除以2得到:

N*(N-1)/4

複製的次數大致等於比較的次數,然而,一次複製與一次比較的時間消耗不同,所以相對於隨機數據,這個算法比冒泡排序快一倍,比選擇排序略快。

與冒泡排序、選擇排序一樣,插入排序的時間複雜度仍然爲O(N2),這三者被稱爲簡單排序或者基本排序。

如果待排序數組基本有序時,插入排序的效率會更高。

算法改進:

 

在插入某個元素之前需要先確定該元素在有序數組中的位置,上例的做法是對有序數組中的元素逐個掃描,當數據量比較大的時候,這是一個很耗時間的過程,可以採用二分查找法改進,這種排序也被稱爲二分插入排序

改進後的代碼如下:

def _binary_search(arr, l, r, target):   #基於二分查找來找到插入點, 但不能直接照搬二分查找的算法。 arr:數組;l,r:查找的左,右端點索引值,target:目標值
    while l < r:   
        if r - l == 1:   #當搜索範圍 r - l == 1時,開始判斷插入點的具體索引
            if arr[l] > target:  
                return l
            else:
                return r
        ind = (l + r)//2
        if arr[ind] > target:
            r = ind
        else:
            l = ind


def insert_sort_improve(arr, nums):
    for i in range(1, nums):
        j = i - 1
        if arr[i] < arr[j]:
            temp = arr[i]
            ind = _binary_search(arr, 0, j, arr[i])
            while j >= ind:
                arr[j+1] = arr[j]
                j -= 1
            arr[ind] = temp

插入排序的改進算法還有很多,如希爾排序,2-路插入排序等。在次不再一一敘述。

插入排序,在數組基本有序的情況下,性能優異。對於較大數組的排序,一般使用快速排序與插入排序組合。可進一步提升快速排序的性能。

發佈了20 篇原創文章 · 獲贊 27 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章