排序是查找是算法中最重要的兩個概念,咱們大多數狀況下都在進行查找和排序。科學家們窮盡努力,想使得排序和查找可以更加快速。本篇文章用Python實現十大排序算法。
排序算法從不一樣維度能夠分爲好多類別,從其排序思想(排序思想通常決定了其時間複雜度的量級)來看,主要能夠分爲四類:python
- 雙層循環比較排序:平方級排序
- 分治策略比較排序:對數級排序
- 另闢蹊徑的非比較方式排序:線性級排序
- 笑死人不償命的其它排序:有着天馬行空的時間複雜度,難以描述。
冒泡排序git
- 從數組的第一個元素開始,比較當前元素和下一個元素,若是當前元素大於下一個元素,交換兩元素位置。
- 接着從第二個元素開始,重複第一步,直到當前元素爲最後一個元素。此時最後一個元素爲最大元素。未排序數組爲除最後一個元素以外的其它元素。
- 對未排序數組不斷重複以上步驟,直到未排序數組爲空。
def bubble_sort(arr): length = len(arr) for i in range(length): for j in range(length-i-1): if arr[j] > arr[j+1]: arr[j], arr[j+1] = arr[j+1], arr[j] return arr
選擇排序github
- 選取數組中的最小元素,和數組中的第一個元素交換位置
- 選取數組中除第一個元素外剩餘元素的最小元素,和數組中的第二個元素交換位置。
- 不斷重複以上步驟,直到當前選取的元素爲數組中最後一個元素。
def select_sort(arr): length = len(arr) for i in range(length): min_ix = i for j in range(i, length): if arr[j] < arr[min_ix]: min_ix = j arr[min_ix], arr[i] = arr[i], arr[min_ix] return arr
插入排序算法
- 從數組的第一個元素開始,不斷比較當前元素和前一個元素。若是當前元素比前一個元素小,那麼就將當前元素插入到前一個元素的前面(即二者交換位置)
- 從第二個元素開始,不斷重複以上步驟,直到全部元素所有經歷上述步驟。
def insert_sort(arr): length = len(arr) for i in range(length): for j in range(i, 0, -1): if arr[j] < arr[j-1]: arr[j], arr[j-1] = arr[j-1], arr[j] return arr
希爾排序shell
- 選擇一個增量值k,分別將數組中索引以k爲間隔的元素放在同一個數組中。
- 將增量值縮小爲原增量值的1/2,而後重複步驟1。
- 直到增量值爲1,使用插入排序對已經部分有序的數組進行排序。
def shell_sort(arr): n = len(arr) gap = int(n/2) while gap > 0: for i in range(gap,n): temp = arr[i] j = i while j >= gap and arr[j-gap] >temp: arr[j] = arr[j-gap] j -= gap arr[j] = temp gap = int(gap/2) return arr
歸併排序api
- 以數組中間元素爲界,將數組分爲等長的兩個數組(可能不等長,和數組長度的奇偶性有關)。
- 對全部數組執行步驟1
- 不斷重複以上步驟,直到將數組分割爲多個包含單個元素的數組。
- 將以上數組兩兩合併,並排序,此時爲多個包含有序的兩個元素的數組(可能包含單個元素,跟數組長度的奇偶性有關)。
- 重複步驟4,直到將全部數組合併爲一個數組
def merge(left, right): i = j = 0 res = [] while i < len(left) and j < len(right): if left[i] < right[j]: res.append(left[i]) i += 1 else: res.append(right[j]) j += 1 if i == len(left): res.extend(right[j:]) else: res.extend(left[i:]) return res def merge_sort(arr): if len(arr) <= 1: return arr length = len(arr) i = int(length / 2) left = merge_sort(arr[:i]) right = merge_sort(arr[i:]) return merge(left, right)
快速排序數組
- 挑選一個元素爲基準
- 比基準大的元素做爲一個數組,比基準小或者等於基準的元素做爲一個數組。
- 對新分割的數組,不斷重複以上步驟,直到分割後的數組只含有1個或者0個元素
- 遞歸地合併以上數組爲有序數組,合併方式爲:[小於等於基準的元素]+[基準]+[大於基準的元素]
def fast_sort(arr): if len(arr) <= 1: return arr pivot = arr.pop() left = [i for i in arr if i <= pivot] right = [i for i in arr if i > pivot] return fast_sort(left) + [pivot] + fast_sort(right)
以上算法須要額外的空間,若是咱們將小於等於基準的元素不斷置於基準元素以前,大於基準的元素置於基準元素以後,那麼就能夠實現不須要額外空間的就地排序。app
def fast_sort_on_extra_spacing(arr): l = 0 h = len(arr)-1 def partition(arr, l, h): pivot = arr[h] for i in range(l, h): if arr[i] <= pivot: arr[l], arr[i] = arr[i], arr[l] l += 1 arr[h], arr[l] = arr[l], arr[h] return l def fast_sort(arr, l, h): if l < h: pivot = partition(arr, l, h) fast_sort(arr, l, pivot-1) fast_sort(arr, pivot+1, h) return arr return fast_sort(arr, l, h)
堆排序學習
- 先對待排序數組構造大根堆
- 將大根堆第一個元素和最後一個元素交換位置。此時最後一個元素爲最大元素,待排序數組爲除最後一個元素以外的全部元素。
- 對待排序數組不斷重複以上步驟,直到待排序數組中只有一個元素。
def heapify(arr, n, i): # build a max root heap max_ix = i left_i = 2 * i + 1 right_i = 2 * i + 2 if left_i < n and arr[max_ix] < arr[left_i]: max_ix = left_i if right_i < n and arr[max_ix] < arr[right_i]: max_ix = right_i if max_ix != i: arr[max_ix], arr[i] = arr[i], arr[max_ix] heapify(arr, n, max_ix) def heap_sort(arr): for i in range(n-1, -1, -1): heapify(arr, n, i) for i in range(n-1, 0, -1): arr[i], arr[0] = arr[0], arr[i] heapify(arr, i, 0) return arr
此排序方法只適用於數組元素所有爲整數的情景。
計數排序ui
- 找出待排序數組中最大的元素,構造一個長度爲此元素值的計數數組。
- 遍歷待排序數組元素,以當前元素爲索引,將計數數組中的對應值加1.
- 此時計數數組中的索引爲待排序數組中的元素,值爲出現的次數。將計數數組中全部值非0的元素索引根據其出現次數串聯起來。
def count_sort(arr): min_ix, max_ix = min(arr), max(arr) bucket = [0 for _ in range(max_ix+1)] for i in arr: bucket[i] += 1 return sum([[i] * bucket[i] for i in range(len(bucket)) if bucket[i] != 0], [])
桶排序
- 設置固定數量的桶(這是個技術活兒).
- 將待排序數組中的元素放入對應的桶中(對應關係也是個技術活兒,下面的例子中採用整除)
- 將非空桶中的元素串聯起來。
def bucket_sort(arr): min_ix, max_ix = min(arr), max(arr) bucket_range = (max_ix - min_ix) / len(arr) # +1 avoid for that max_ix - min_ix will raise a IndexError temp_bucket = [[] for i in range(len(arr) + 1)] for i in arr: temp_bucket[int((i-min_ix)//bucket_range)].append(i) return sum(temp_bucket, [])
基數排序
- 找出待排序數組中最大元素的位數。將全部元素補足此位數,補足方式爲前面補0。
- 從最低位到最高位,進行多輪數組排序。
def radix_sort(arr): max_value = max(arr) num_digits = len(str(max_value)) for i in range(num_digits): bucket = [[] for _ in range(10)] for j in arr: bucket[j//(10**i)%10].append(j) arr = [j for i in bucket for j in i] return arr
睡排序
讓多個進程(線程)分別睡眠待排序數組中的元素時長,先睡醒的進程(線程),對應元素追加到結果數組中。
猴子排序
不停隨機排序,而後檢查是否元素所有有序。若是你是歐皇,那麼你能夠嘗試用這個排序算法,極可能一次搞定。
算法 | 平均時間複雜度 | 最優時間複雜度 | 最壞時間複雜度 | 空間複雜度 | 是否原地排序 | 是否穩定 | 是否通用 |
---|---|---|---|---|---|---|---|
冒泡排序 | O(n2) | O(n) | O(n2) | O(1) | 是 | 是 | 是 |
選擇排序 | O(n2) | O(n2) | O(n2) | O(1) | 是 | 否 | 是 |
插入排序 | O(n2) | O(n) | O(n2) | O(1) | 是 | 是 | 是 |
希爾排序 | O(n logn) | O(n log2n) | O(n log2n) | O(1) | 是 | 否 | 是 |
歸併排序 | O(n logn) | O(n logn) | O(n logn) | O(n) | 否 | 是 | 是 |
快速排序 | O(n logn) | O(n logn) | O(n2) | O(n logn) | 是 | 否 | 是 |
堆排序 | O(n logn) | O(n logn) | O(n logn) | O(1) | 是 | 否 | 是 |
計數排序 | O(n+k) | O(n+k) | O(n+k) | O(k) | 否 | 是 | 否 |
桶排序 | O(n+k) | O(n+k) | O(n2) | O(n+k) | 否 | 是 | 否 |
基數排序 | O(n*k) | O(n*k) | O(n*k) | O(n+k) | 否 | 是 | 否 |
排序算法是算法學習中的核心。掌握排序算法及其思想是學習其它算法的基礎。但願你們能夠熟練掌握。歡迎關注我的博客:藥少敏的博客。