經典排序算法(冒泡排序,選擇排序,插入排序,快速排序,堆排序)python實現

最近在復習經典排序算法,自己用python也實現了一下,這里不會涉及到原理(因為網上方法已經很詳細啦),就把函數貼上來,可以讓大家自己試著運行下,再結合別處的原理也可以更好地理解它們的實現。
如果有錯誤請指出,或者優化的地方,謝謝啦。(′▽`)

1. 冒泡排序

冒泡排序是實現起來最簡單的排序算法,時間復雜度是O(n^2),它的代碼核心是兩層嵌套的for循環,循環里一個判斷數組相鄰兩個元素大小,如果不滿足就交換。
冒泡排序有一個小的優化的點:如果在外層循環的一趟里沒有交換任何元素,就說明排序完成了,就不需要再繼續排了。所以也設置了一個flag判斷。
代碼如下:

"""
bubble sorting
"""
 def bubble_sorting(array):
    for i in range(len(array)-1):
        flag = False
        for j in range(len(array)-i-1):
            if array[j]>array[j+1]:
                flag = True
                tmp = array[j]
                array[j] = array[j+1]
                array[j+1] = tmp
        if not flag:
            break
    return array
array = [0,1,0,7,9,8,5,4,2,0]
print(bubble_sorting(array))

運行結果就是[0, 0, 0, 1, 2, 4, 5, 7, 8, 9]

2. 選擇排序

選擇排序也是一種時間復雜度是O(n^2)的穩定排序算法,它的核心思想就是每次遍歷數組選出最小的放在左邊 ,這樣右邊無序區越來越小,左邊有序區越來越大。也是兩層循環嵌套。
代碼如下:

def select_sorting(array):
    for i in range(len(array)):
        min = array[i]
        min_index = i
        for j in range(i,len(array)):
            if array[j]<min:
                min = array[j]
                min_index = j
        array[min_index] = array[i]
        array[i] = min
    return array
print(select_sorting(array))

運行結果 也是[0, 0, 0, 1, 2, 4, 5, 7, 8, 9]

3. 插入排序

插入排序跟上面兩種排序算法比起來就算有點點繞了,它雖然也是需要兩層循環,但是內層不再是遍歷。插入排序要注意的是外層循環從第二個值開始,把第一個值當作是有序區。這樣每一個新的值都會跟前面的有序區進行比較,如果小于前一個數就插到前面,一直到合適的位置為止。

def insert_sorting(array):
    for i in range(1,len(array)):
        j = i-1
        while j >= 0 and array[j]>array[j+1]:
            tmp = array[j+1]
            array[j+1] = array[j]
            array[j] = tmp
            j -= 1
    return array
print(insert_sorting(array))

7-28修改:之前還是覺得內層循環并不是插入排序,而是冒泡排序了,實際上并不需要每一個結果都要和上一個交換,因為插入排序應該是新的數和前面有序區的每一個數比較,如果仍然大于就把前面的數挪到后面,最后再插入新數。所以算法優化如下。
這里面的最后j+1其實也卡了我很久,但是后來想到:循環退出時一定不滿足條件了,要么是j小于零,要么是終于找到了合適的位置。這讓我想到一個小的腦筋急轉彎:如果一個新的數比第二個數小,那么應該插入到哪里?答案是第二個。所以最后要把1加回來。

def insert_sorting1(array):
    for i in range(1,len(array)):
        j = i-1
        tmp = array[i]
        while j >= 0 and array[j]>tmp:
            array[j+1] = array[j]
            j -= 1
        array[j+1] = tmp
    return array

print(insert_sorting1([3,5,4,2,9,1,1,7,8,4]))

4. 快速排序

快速排序……卡了我很久很久,因為我一直在嘗試用循環遞推做,我……失敗了。快排本身是一種很常用(超重要,面試也經常考的排序算法),它是一種“分治”的思想,就是要用遞歸的意思,接受了這一點再來寫,就不會自己難為自己了。它始終選一個基準,一般是左邊第一個,然后注意從右往左推,找到比他更小的就停止,然后從左往右推,找到比他更大的停止,然后判斷下左右兩個指針仍未相遇的話,就交換左右的值。此時左邊都比基準小,右邊都比基準大。再對左右兩個子列表分別用這個方法,直到整個列表都有序。
接下來獻上我很丑的代碼:

def quick_sorting(start, end, array):
   if start >= end:
       return
   min = start
   max = end
   pivot = array[min]
   while start < end:
       while start < end and array[end] > pivot:
           end -= 1
       while start < end and array[start] < pivot:
           start += 1
       if start < end:
           array[start], array[end] = array[end], array[start]

   quick_sorting(min, start-1, array)
   quick_sorting(start+1,max,array)

   return array

假如你并不需要保留重復的項,或者需要排序的列表沒有重復項,那么可以用一個真正簡單美麗的遞歸解決問題,其實這個就是剛才的算法的簡化:

def quick_sort(array):
    if len(array)<2:
        return array
    pivot = array[0]
    left = [i for i in array if i < pivot]
    right = [i for i in array if i > pivot]
    return quick_sort(left)+[pivot]+quick_sort(right)
print(quick_sort(array))

7-25 修改:

剛剛想到一個方法可以使上面的快排保留重復的值,只要做一點小的變化:

def quick_sorting(array):
    if len(set(array))<2:
        return array
    middle = array[0]
    left = [i for i in array if i <middle]
    pivot = [i for i in array if i == middle]
    right = [i for i in array if i > middle]

    return quick_sorting(left)+quick_sorting(pivot)+quick_sorting(right)

print(quick_sorting([9,9,1,8,5,2,3,3,7,14,2,2,2,2]))

所以以后偷懶的快排就可以這么寫啦。

5. 堆排序(堆的基本操作)

我是按照小灰這一篇堆排序的文章寫的代碼,可以參考:
漫畫:什么是二叉堆?(修正版)
總結起來,堆排序首先要理解二叉堆,大頂堆,小頂堆的概念,然后堆的基本操作有:插入(向上調整),刪除(向下調整),調整二叉堆等。
希望大家有空可以讀一下小灰的那篇文章~寫得很清楚了,我這里就貼自己寫得代碼和用例吧。

# coding:utf-8
import sys

class HeapSorting():

    def __init__(self,array):
        self.arr = array

    def insert(self,value):
        child_index = len(self.arr)
        self.arr.append(value) # add the new value at the end of array
        tmp = self.arr[child_index]
        while child_index >= 1 :
            parent_index = (child_index - 1) // 2
            if self.arr[parent_index] < tmp:
                break
            self.arr[child_index] = self.arr[parent_index]
            child_index = parent_index
        array[child_index] = tmp

        return self.arr


    def delete(self):
        # delete the first value, make the last one the top
        self.arr[0] = self.arr[-1]
        del self.arr[-1]
        parent_index = 0
        child_index = parent_index*2+1
        while child_index <= len(self.arr)-1:
            child_index = parent_index * 2 + 2 if parent_index * 2 + 2 <= len(self.arr) - 1 and self.arr[
                parent_index * 2 + 2] < self.arr[parent_index * 2 + 1] else parent_index * 2 + 1
            if self.arr[parent_index] < self.arr[child_index]:
                break
            tmp = self.arr[parent_index]
            self.arr[parent_index] = self.arr[child_index]
            self.arr[child_index] = tmp
            parent_index = child_index
            child_index = parent_index*2+1
        return self.arr

    def down_adjust(self,parent_index):

        child_index = parent_index*2+1
        while child_index <= len(self.arr)-1:
            child_index = parent_index * 2 + 2 if parent_index * 2 + 2 <= len(self.arr) - 1 and self.arr[
                parent_index * 2 + 2] < self.arr[parent_index * 2 + 1] else parent_index * 2 + 1
            if self.arr[parent_index] < self.arr[child_index]:
                break
            tmp = self.arr[parent_index]
            self.arr[parent_index] = self.arr[child_index]
            self.arr[child_index] = tmp
            parent_index = child_index
            child_index = parent_index*2+1
        return self.arr

    def adjust(self):
        # start from the last non-leaf node
        node = (len(self.arr)-2)//2

        for i in range(node,-1,-1):
            self.arr = self.down_adjust(i)
        return self.arr

array = [7, 2, 6, 3, 9, 8, 5, 10, 12]
heap = HeapSorting(array)
heap_insert = heap.insert(1)
heap_delete = heap.delete() # delete the value on top of the heap
heap_adjust = heap.adjust()

print(heap_adjust)

上面的操作對于理解堆來說非常重要。
過兩天還會補上正式的堆排序~就先寫到這里(鞠躬)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,024評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,285評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,548評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,328評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,878評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,098評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,616評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,554評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,725評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,971評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,361評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,613評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,339評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,695評論 2 370

推薦閱讀更多精彩內容