排序算法——(2)Python實現十大經常使用排序算法

上期爲你們講解了排序算法常見的幾個概念:html

  1. 相關性:排序時是否須要比較元素
  2. 穩定性:相同元素排序後是否可能打亂
  3. 時間空間複雜度:隨着元素增長時間和空間隨之變化的函數

若是有遺忘的同窗能夠看排序算法——(1)簡介這篇文章複習一下。python

今天將爲你們介紹經常使用的十大排序算法中最簡單的五種(冒泡、選擇、插入、希爾、歸併),主要從:過程圖解、算法思想、代碼實現、算法分析這四個方面講解,建議你們看完以後本身動手練習增強記憶!
排序一覽表
注:本文使用的複雜度均爲最壞複雜度算法

1、冒泡排序

冒泡排序(Bubble Sort),是一種計算機科學領域的較簡單的排序算法。它重複地走訪過要排序的元素列,依次比較兩個相鄰的元素,一層一層的將較大的元素日後移動,其現象和睦泡在上升過程當中慢慢變大相似,故成爲冒泡排序。shell

1.過程圖解

冒泡排序

2.算法思想

  1. 從第一個和第二個開始比較,若是第一個比第二個大,則交換位置,而後比較第二個和第三個,逐漸日後
  2. 通過第一輪後最大的元素已經排在最後,因此重複上述操做的話第二大的則會排在倒數第二的位置。
  3. 那重複上述操做n-1次便可完成排序,由於最後一次只有一個元素因此不須要比較

3.代碼實現

def bubble_sort(arr): """冒泡排序""" # 第一層for表示循環的遍數 for i in range(len(arr) - 1): # 第二層for表示具體比較哪兩個元素 for j in range(len(arr) - 1 - i): if arr[j] > arr[j + 1]: # 若是前面的大於後面的,則交換這兩個元素的位置 arr[j], arr[j + 1] = arr[j + 1], arr[j] return arr 

4.算法分析

冒泡排序是一種簡單直接暴力的排序算法,爲何說它暴力?由於每一輪比較可能多個元素移動位置,而元素位置的互換是須要消耗資源的,因此這是一種偏慢的排序算法,僅適用於對於含有較少元素的數列進行排序。數組

  1. 穩定性:咱們從代碼中能夠看出只有前一個元素大於後一個元素纔可能交換位置,因此相同元素的相對順序不可能改變,因此它是穩定排序
  2. 比較性:由於排序時元素之間須要比較,因此是比較排序
  3. 時間複雜度:由於它須要雙層循環n*(n-1)),因此平均時間複雜度爲O(n^2)
  4. 空間複雜度:只須要常數個輔助單元,因此空間複雜度爲O(1),咱們把空間複雜度爲O(1)的排序成爲原地排序(in-place)
  5. 記憶方法:想象成氣泡,一層一層的往上變大

2、選擇排序

選擇排序(Selection sort)是一種簡單直觀的排序算法。它的工做原理是每一次從待排序的數據元素中選出最小(或最大)的一個元素,存放在序列的起始位置,因此稱爲:選擇排序app

1.過程圖解

選擇排序

2.算法思想

  1. 設第一個元素爲比較元素,依次和後面的元素比較,比較完全部元素找到最小的元素,將它和第一個元素互換
  2. 重複上述操做,咱們找出第二小的元素和第二個位置的元素互換,以此類推找出剩餘最小元素將它換到前面,即完成排序

3.代碼實現

def selection_sort(arr): """選擇排序""" # 第一層for表示循環選擇的遍數 for i in range(len(arr) - 1): # 將起始元素設爲最小元素 min_index = i # 第二層for表示最小元素和後面的元素逐個比較 for j in range(i + 1, len(arr)): if arr[j] < arr[min_index]: # 若是當前元素比最小元素小,則把當前元素角標記爲最小元素角標 min_index = j # 查找一遍後將最小元素與起始元素互換 arr[min_index], arr[i] = arr[i], arr[min_index] return arr 

4.算法分析

選擇排序和冒泡排序很相似,可是選擇排序每輪比較只會有一次交換,而冒泡排序會有屢次交換,交換次數比冒泡排序少,就減小cpu的消耗,因此在數據量小的時候能夠用選擇排序,實際適用的場合很是少。ide

  1. 比較性:由於排序時元素之間須要比較,因此是比較排序
  2. 穩定性:由於存在任意位置的兩個元素交換,好比[5, 8, 5, 2],第一個5會和2交換位置,因此改變了兩個5原來的相對順序,因此爲不穩定排序。
  3. 時間複雜度:咱們看到選擇排序一樣是雙層循環n*(n-1)),因此時間複雜度也爲:O(n^2)
  4. 空間複雜度:只須要常數個輔助單元,因此空間複雜度也爲O(1)
  5. 記憶方法:選擇對象要先選最小的,由於嫩,哈哈

3、插入排序

插入排序(Insertion-Sort)的算法描述是一種簡單直觀的排序算法。它的工做原理是經過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。函數

1.過程圖解

插入排序

2.算法思想

  1. 從第二個元素開始和前面的元素進行比較,若是前面的元素比當前元素大,則將前面元素 後移,當前元素依次往前,直到找到比它小或等於它的元素插入在其後面
  2. 而後選擇第三個元素,重複上述操做,進行插入
  3. 依次選擇到最後一個元素,插入後即完成全部排序

3.代碼實現

def insertion_sort(arr): """插入排序""" # 第一層for表示循環插入的遍數 for i in range(1, len(arr)): # 設置當前須要插入的元素 current = arr[i] # 與當前元素比較的比較元素 pre_index = i - 1 while pre_index >= 0 and arr[pre_index] > current: # 當比較元素大於當前元素則把比較元素後移 arr[pre_index + 1] = arr[pre_index] # 往前選擇下一個比較元素 pre_index -= 1 # 當比較元素小於當前元素,則將當前元素插入在 其後面 arr[pre_index + 1] = current return arr 

4.算法分析

插入排序的適用場景:一個新元素須要插入到一組已是有序的數組中,或者是一組基本有序的數組排序。ui

  1. 比較性:排序時元素之間須要比較,因此爲比較排序
  2. 穩定性:從代碼咱們能夠看出只有比較元素大於當前元素,比較元素纔會日後移動,因此相同元素是不會改變相對順序
  3. 時間複雜度:插入排序一樣須要兩次循壞一個一個比較,故時間複雜度也爲O(n^2)
  4. 空間複雜度:只須要常數個輔助單元,因此空間複雜度也爲O(1)
  5. 記憶方法:想象成在書架中插書:先找到相應位置,將後面的書日後推,再將書插入

4、希爾排序

希爾排序(Shell’s Sort)是插入排序的一種又稱「縮小增量(間隔)排序」(Diminishing Increment Sort),是直接插入排序算法的一種更高效的改進版本,它與插入排序的不一樣之處在於,它會優先比較距離較遠的元素,該方法因D.L.Shell於1959年提出而得名。spa

1.過程圖解

希爾排序

2.算法思想

希爾排序的總體思想是將固定間隔的幾個元素之間排序,而後再縮小這個間隔。這樣到最後數列就成爲了基本有序數列,而前面咱們講過插入排序對基本有序數列排序效果較好。

  1. 計算一個增量(間隔)值
  2. 對元素進行增量元素進行比較,好比增量值爲7,那麼就對0,7,14,21…個元素進行插入排序
  3. 而後對1,8,15…進行排序,依次遞增進行排序
  4. 全部元素排序完後,縮小增量好比爲3,而後又重複上述第2,3步
  5. 最後縮小增量至1時,數列已經基本有序,最後一遍普通插入便可

已知的最增量式是由 Sedgewick 提出的 (1, 5, 19, 41, 109,…),該步長的項來自 9 * 4^i - 9 * 2^i + 1 和 4^i - 3 * 2^i + 1 這兩個算式。這項研究也代表 "比較在希爾排序中是最主要的操做,而不是交換。 用這樣增量式的希爾排序比插入排序和堆排序都要快,甚至在小數組中比快速排序還快,可是在涉及大量數據時希爾排序仍是比快速排序慢。

3.代碼實現

def shell_sort(arr): """希爾排序""" # 取整計算增量(間隔)值 gap = len(arr) // 2 while gap > 0: # 從增量值開始遍歷比較 for i in range(gap, len(arr)): j = i current = arr[i] # 元素與他同列的前面的每一個元素比較,若是比前面的小則互換 while j - gap >= 0 and current < arr[j - gap]: arr[j] = arr[j - gap] j -= gap arr[j] = current # 縮小增量(間隔)值 gap //= 2 return arr 

4.算法分析

  1. 比較性:排序時元素之間須要比較,因此爲比較排序
  2. 穩定性:由於希爾排序是間隔的插入,因此存在相同元素相對順序被打亂,因此是不穩定排序
  3. 時間複雜度: 最壞時間複雜度O(n2)平均複雜度爲O(n1.3)
  4. 空間複雜度:只須要常數個輔助單元,因此空間複雜度也爲O(1)
  5. 記憶方法:插入排序是每輪都是一小步,希爾排序是先大步後小步,它第一個突破O(n2)的排序算法。聯想起阿姆斯特朗登月以後說:這是我我的一小步,倒是人類邁出的一大步。

5、歸併排序

歸併排序(MERGE-SORT)是創建在歸併操做上的一種有效的排序算法,該算法是採用分治法(Divide and Conquer)的一個很是典型的應用。歸併排序適用於子序列有序的數據排序。

1.過程圖解

歸併排序

2.算法思想

從上圖看分解後的數列很像一個二叉樹。

  1. 使用遞歸將源數列使用二分法分紅多個子列
  2. 申請空間將兩個子列排序合併而後返回
  3. 將全部子列一步一步合併最後完成排序

3.代碼實現

def merge_sort(arr): """歸併排序""" if len(arr) == 1: return arr # 使用二分法將數列分兩個 mid = len(arr) // 2 left = arr[:mid] right = arr[mid:] # 使用遞歸運算 return marge(merge_sort(left), merge_sort(right)) def marge(left, right): """排序合併兩個數列""" result = [] # 兩個數列都有值 while len(left) > 0 and len(right) > 0: # 左右兩個數列第一個最小放前面 if left[0] <= right[0]: result.append(left.pop(0)) else: result.append(right.pop(0)) # 只有一個數列中還有值,直接添加 result += left result += right return result 

4.算法分析

  1. 比較性:排序時元素之間須要比較,因此爲比較排序
  2. 穩定性:咱們從代碼中能夠看到當左邊的元素小於等於右邊的元素就把左邊的排前面,而本來左邊的就是在前面,因此相同元素的相對順序不變,故爲穩定排序
  3. 時間複雜度: 複雜度爲O(nlog^n)
  4. 空間複雜度:在合併子列時須要申請臨時空間,並且空間大小隨數列的大小而變化,因此空間複雜度爲O(n)
  5. 記憶方法:所謂歸併確定是要先分解,再合併

總結

今天給你們介紹的五種排序是比較簡單的排序,建議你們本身動手敲幾遍代碼,書讀百遍,其義自現。要求你們必須理解&記住它們的算法原理,由於代碼是永遠記不住的,只要記住原理你就能用僞代碼實現。 爲了方便你們記憶我在每一個算法分析最後給出了本身的記憶方法,若是你有不理解的地方,歡迎在下方留言,同時也歡迎你們轉發分享!

相關文章
相關標籤/搜索