1. 程式人生 > >基於python的七種經典排序演算法

基於python的七種經典排序演算法

參考書目:《大話資料結構》

一、排序的基本概念和分類

所謂排序,就是使一串記錄,按照其中的某個或某些關鍵字的大小,遞增或遞減的排列起來的操作。排序演算法,就是如何使得記錄按照要求排列的方法。

排序的穩定性:
經過某種排序後,如果兩個記錄序號同等,且兩者在原無序記錄中的先後秩序依然保持不變,則稱所使用的排序方法是穩定的,反之是不穩定的。

內排序和外排序
內排序:排序過程中,待排序的所有記錄全部放在記憶體中
外排序:排序過程中,使用到了外部儲存。
通常討論的都是內排序。

影響內排序演算法效能的三個因素

  • 時間複雜度:即時間效能,高效率的排序演算法應該是具有儘可能少的關鍵字比較次數和記錄的移動次數
  • 空間複雜度:主要是執行演算法所需要的輔助空間,越少越好。
  • 演算法複雜性。主要是指程式碼的複雜性。

根據排序過程中藉助的主要操作,可把內排序分為:

  • 插入排序
  • 交換排序
  • 選擇排序
  • 歸併排序

按照演算法複雜度可分為兩類:

  • 簡單演算法:包括氣泡排序、簡單選擇排序和直接插入排序
  • 改進演算法:包括希爾排序、堆排序、歸併排序和快速排序

以下的七種排序演算法只是所有排序演算法中最經典的幾種,不代表全部。

二、 氣泡排序

氣泡排序(Bubble sort):時間複雜度O(n^2)
交換排序的一種。其核心思想是:兩兩比較相鄰記錄的關鍵字,如果反序則交換,直到沒有反序記錄為止。

其實現細節可以不同,比如下面3種:

  1. 最簡單排序實現:bubble_sort_simple
  2. 氣泡排序:bubble_sort
  3. 改進的氣泡排序:bubble_sort_advance
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liu Jiang
# Python 3.5
# 氣泡排序演算法


class SQList:
    def__init__(self, lis=None):
        self.r = lis

    def swap(self, i, j):
        """定義一個交換元素的方法,方便後面呼叫。"""
temp = self.r[i] self.r[i] = self.r[j] self.r[j] = temp def bubble_sort_simple(self): """ 最簡單的交換排序,時間複雜度O(n^2) """ lis = self.r length = len(self.r) for i in range(length): for j in range(i+1, length): if lis[i] > lis[j]: self.swap(i, j) def bubble_sort(self): """ 氣泡排序,時間複雜度O(n^2) """ lis = self.r length = len(self.r) for i in range(length): j = length-2 while j >= i: if lis[j] > lis[j+1]: self.swap(j, j+1) j -= 1 def bubble_sort_advance(self): """ 氣泡排序改進演算法,時間複雜度O(n^2) 設定flag,當一輪比較中未發生交換動作,則說明後面的元素其實已經有序排列了。 對於比較規整的元素集合,可提高一定的排序效率。 """ lis = self.r length = len(self.r) flag = True i = 0 while i < length and flag: flag = False j = length - 2 while j >= i: if lis[j] > lis[j + 1]: self.swap(j, j + 1) flag = True j -= 1 i += 1 def__str__(self): ret = "" for i in self.r: ret += " %s" % i return ret if __name__ == '__main__': sqlist = SQList([4,1,7,3,8,5,9,2,6]) # sqlist.bubble_sort_simple() # sqlist.bubble_sort() sqlist.bubble_sort_advance() print(sqlist)

三、簡單選擇排序

簡單選擇排序(simple selection sort):時間複雜度O(n^2)
通過n-i次關鍵字之間的比較,從n-i+1個記錄中選出關鍵字最小的記錄,並和第i(1<=i<=n)個記錄進行交換。

通俗的說就是,對尚未完成排序的所有元素,從頭到尾比一遍,記錄下最小的那個元素的下標,也就是該元素的位置。再把該元素交換到當前遍歷的最前面。其效率之處在於,每一輪中比較了很多次,但只交換一次。因此雖然它的時間複雜度也是O(n^2),但比冒泡演算法還是要好一點。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liu Jiang
# Python 3.5
# 簡單選擇排序


class SQList:
    def__init__(self, lis=None):
        self.r = lis

    def swap(self, i, j):
        """定義一個交換元素的方法,方便後面呼叫。"""
        temp = self.r[i]
        self.r[i] = self.r[j]
        self.r[j] = temp

    def select_sort(self):
        """        簡單選擇排序,時間複雜度O(n^2)        """
        lis = self.r
        length = len(self.r)
        for i in range(length):
            minimum = i
            for j in range(i+1, length):
                if lis[minimum] > lis[j]:
                    minimum = j
            if i != minimum:
                self.swap(i, minimum)

    def__str__(self):
        ret = ""
        for i in self.r:
            ret += " %s" % i
        return ret

if __name__ == '__main__':
    sqlist = SQList([4, 1, 7, 3, 8, 5, 9, 2, 6, 0])
    sqlist.select_sort()
    print(sqlist)

四、直接插入排序

直接插入排序(Straight Insertion Sort):時間複雜度O(n^2)
基本操作是將一個記錄插入到已經排好序的有序表中,從而得到一個新的、記錄數增1的有序表。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liu Jiang
# Python 3.5
# 直接插入排序


class SQList:
    def__init__(self, lis=None):
        self.r = lis

    def insert_sort(self):
        lis = self.r
        length = len(self.r)
        # 下標從1開始
        for i in range(1, length):
            if lis[i] < lis[i-1]:
                temp = lis[i]
                j = i-1
                while lis[j] > temp and j >= 0:
                    lis[j+1] = lis[j]
                    j -= 1
                lis[j+1] = temp

    def__str__(self):
        ret = ""
        for i in self.r:
            ret += " %s" % i
        return ret

if __name__ == '__main__':
    sqlist = SQList([4, 1, 7, 3, 8, 5, 9, 2, 6, 0])
    sqlist.insert_sort()
    print(sqlist)

該演算法需要一個記錄的輔助空間。最好情況下,當原始資料就是有序的時候,只需要一輪對比,不需要移動記錄,此時時間複雜度為O(n)。然而,這基本是幻想。

五、希爾排序

希爾排序(Shell Sort)是插入排序的改進版本,其核心思想是將原資料集合分割成若干個子序列,然後再對子序列分別進行直接插入排序,使子序列基本有序,最後再對全體記錄進行一次直接插入排序。

這裡最關鍵的是跳躍和分割的策略,也就是我們要怎麼分割資料,間隔多大的問題。通常將相距某個“增量”的記錄組成一個子序列,這樣才能保證在子序列內分別進行直接插入排序後得到的結果是基本有序而不是區域性有序。下面的例子中通過:increment = int(increment/3)+1來確定“增量”的值。

希爾排序的時間複雜度為:O(n^(3/2))

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liu Jiang
# Python 3.5
# 希爾排序


class SQList:
    def__init__(self, lis=None):
        self.r = lis

    def shell_sort(self):
        """希爾排序"""
        lis = self.r
        length = len(lis)
        increment = len(lis)
        while increment > 1:
            increment = int(increment/3)+1
            for i in range(increment+1, length):
                if lis[i] < lis[i - increment]:
                    temp = lis[i]
                    j = i - increment
                    while j >= 0 and temp < lis[j]:
                        lis[j+increment] = lis[j]
                        j -= increment
                    lis[j+increment] = temp

    def__str__(self):
        ret = ""
        for i in self.r:
            ret