1 冒泡排序
算法思想:從第一個開始,相鄰兩個數進行比較,若是前1個數大於後一個數,則進行調換,這樣換到最後,最大的那個就會在最後面,重複這個過程,較大的就會逐個累積在後面,本質思想就是大的在一輪中會逐漸冒泡到後排。
python代碼實現:python
def bubbling_sort(num): if not num or len(num) <= 1: return num_len = len(num) for i in range(num_len-1): swap_flag = True for j in range(0, num_len-i-1): if num[j] > num[j+1]: tmp = num[j] num[j] = num[j+1] num[j+1] = tmp swap_flag=False if swap_flag: return
算法時間複雜度:O(n²)
算法空間複雜度:O(1)
算法穩定性:穩定
算法穩定性概念:假設在數列中存在a[i]=a[j],若在排序以前,a[i]在a[j]前面;而且排序以後,a[i]仍然在a[j]前面。則這個排序算法是穩定的算法
2 選擇排序
算法思想:從第一個開始,而後從下一個開始遍歷一遍找到最小的,而後跟第一個進行交換,這樣就完成一輪,而後選擇第二個,開始第二輪,直到num_len-1輪
python代碼實現:shell
def chose_sort(num): if not num or len(num) <= 1: return num_len = len(num) for i in range(num_len-1): j_tmp = i min = num[i] for j in range(i+1, num_len): if num[j] < min: j_tmp = j min = num[j] if i != j_tmp: tmp = num[i] num[i] = num[j_tmp] num[j_tmp] = tmp
算法複雜度:O(n²)
算法空間複雜度:O(1)
算法穩定性:不穩定(好比,5,5,2;則第一個5會與2交換)數組
3 插入排序
算法思想:相似於打撲克牌拿牌的時候,拿到第一張,而後拿第二張,若是第二張小於第一張則放到第一張的前面,拿到第三張,若是小於第二張,則第三張放到第二張的位置,本來的第二張及之後的都日後退一步。
python代碼實現:app
def insert_sort(num): if not num or len(num) <= 1: return num_len = len(num) for i in range(1, num_len): for j in range(0, i+1): if num[i] < num[j]: break if j <= i: tmp_i = i x = num[i] while tmp_i > j: tmp = num[tmp_i] num[tmp_i] = num[tmp_i-1] num[tmp_i-1] = x tmp_i -= 1 num[j] = x
算法複雜度:O(n²)
算法空間複雜度:O(1)
算法穩定性:穩定函數
4 快速排序
標準算法思想:選中第一個做爲基準參考,將其保存到X變量中,而後這裏當成是挖了一個坑,而後兩個變量分別指定到列表開頭序號+1 start_tmp和列表序號結尾end_tmp,從end_tmp開始與X對比,當num[end_tmp]大於等於X時,end_tmp往前挪一步,而後繼續比較,不然咱們就把它放到坑上,而後當前end_tmp這裏變成是一個坑,end_tmp日後挪一步即-1,而後換到start_tmp那邊進行比較,當num[end_tmp]小於等於X時咱們就把start_tmp往前挪一步即+1,不然就把它放到坑上,而後當前start_tmp變成一個坑,start_tmp往前挪一步,又換到end_tmp那邊,如此反覆直到start_tmp>=end_tmp時結束,此時把X值保存到坑上,這樣就會以X保存的值爲標準,將小於等於X值的都放到左邊,大於等於X值得都放到右邊,而後再分別對兩邊再執行如上操做便可。
python代碼遞歸實現:ui
def quick_sort(num, start=0, end=0): if not num or len(num) <= 1 or start > end: return if start == 0 and end == 0: end = len(num) - 1 start_tmp = start end_tmp = end x = num[start_tmp] while start_tmp < end_tmp: while start_tmp < end_tmp and num[end_tmp] >= x: end_tmp -= 1 if start_tmp < end_tmp: num[start_tmp] = num[end_tmp] start_tmp += 1 while start_tmp < end_tmp and num[start_tmp] <= x: start_tmp += 1 if start_tmp < end_tmp: num[end_tmp] = num[start_tmp] end_tmp -= 1 num[start_tmp] = x quick_sort(num, start, start_tmp - 1) quick_sort(num, start_tmp + 1, end) num = [3, 5, 1, 7, 2, 8, 4, 100, 76, 78] quick_sort(num) print num
算法複雜度:O(nlgn)
算法空間複雜度:O(1)
算法穩定性:不穩定(不少交換的)
該算法的時間複雜度能夠這樣理解:假設有n個數字要排序,每一次的劃分紅左右兩邊數列就相似於樹的一個節點分紅了兩個子樹,每一層就是一次複雜度n的比較排序,直到劃分到最後一層葉子節點時即排完序,假設樹的高度爲x,根據滿二叉樹的性質,2的x次方等於n,則x=lgn,因此算法平均複雜度是nlgn,爲啥說是平均複雜度呢,由於實際排序時並非這麼好,每次都剛好分到左右兩子樹兩等分,從樹的角度上來講就是滿二叉樹是最好的,但有可能出現全是1度節點這種,那麼數的高度就變成n了,複雜度直接變爲n²,在要排序的數一開始是有序的就會造成這樣的結果。spa
兩頭交換思想:選取一箇中間的值做爲基準,而後左邊找一個大於等於該基準的值,右邊找一個小於等於該基準的值,找到後若是左邊序號小於等於右邊序號則進行交換,並左邊序號+1,右邊序號-1,直到左邊序號大於右邊序號時退出,這樣最後也是以基準值爲區分把小於等於基準值的放到左邊,大於等於基準值的放到右邊,而後再分別遞歸。注意while start_tmp <= end_tmp這裏要取<=號,由於下面分別遞歸調用時纔不會有重疊,也就是左邊的start_tmp和右邊的end_tmp不要相等,要讓它們再互相走多一步。
python代碼實現:code
def quick_sort2(num, start=0, end=0): if not num or len(num) <= 1 or start > end: return if start == 0 and end == 0: end = len(num) - 1 start_tmp = start end_tmp = end x = num[(start_tmp + end_tmp) / 2] while start_tmp <= end_tmp: while num[start_tmp] < x: start_tmp += 1 while num[end_tmp] > x: end_tmp -= 1 if start_tmp <= end_tmp: tmp = num[start_tmp] num[start_tmp] = num[end_tmp] num[end_tmp] = tmp start_tmp += 1 end_tmp -= 1 if start < end_tmp: quick_sort2(num, start, end_tmp) if start_tmp < end: quick_sort2(num, start_tmp, end)
算法複雜度:O(nlgn)blog
5 歸併排序
算法思想:是一種分治思想,歸併的歸是遞歸分解數列,並是合併有序數列
python代碼實現:
def merge_array(num, first, mid, last, num_tmp): i = first j = mid+1 tmp = 0 while i <= mid and j <= last: if num[i] <= num[j]: num_tmp[tmp] = num[i] i += 1 else: num_tmp[tmp] = num[j] j += 1 tmp += 1 while i <= mid: num_tmp[tmp] = num[i] i += 1 tmp += 1 while j <= last: num_tmp[tmp] = num[j] j += 1 tmp += 1 for k in range(0, tmp): num[first + k] = num_tmp[k] def rec_merge_sort(num, first, last, num_tmp): if first < last: mid = (first + last) / 2 rec_merge_sort(num, first, mid, num_tmp) rec_merge_sort(num, mid+1, last, num_tmp) merge_array(num, first, mid, last, num_tmp) def merge_sort(num): if not num or len(num) <= 1: return num_len = len(num) num_tmp = [0] * num_len rec_merge_sort(num, 0, num_len-1, num_tmp) num = [3, 5, 1, 7, 2, 8, 4, 100, 76, 78] merge_sort(num) print num
算法複雜度:O(nlgn)
算法空間複雜度:O(n)
算法穩定性:穩定
該算法的時間複雜度能夠這樣理解:假設要比較的數有n個,把要排序的數字當作是徹底二叉樹的葉子節點,合併時就至關於往上構建一顆樹,直到根節點,每一層都執行了n次比較,到數的根節點時,比較完成,層數設爲x,根據滿二叉樹的特性,則2的x次方等於N-1,因此x=lgn,所以總的算法複雜度是nlgn
這裏順便講一下二叉樹的一些關係:
假設2度節點(即有兩個子節點)有x個,1度節點有y個,葉子結點有z個,總的節點個數有N個,則很明顯有x + y + z = N
觀察下二叉樹能夠知道每一個節點都對應一個樹枝,除了根節點,因此有N-1個樹枝,由於二度節點有2個樹枝,1度節點有1個樹枝,葉子節點沒有樹枝,因此有2x + y = N - 1
聯合上面兩個式子能夠知道z = x + 1,也就是葉子節點的個數老是等於2度節點的個數+1
若是是滿二叉樹,則y=0,則2x + 1 = N,則知道N就能夠計算出葉子節點和非葉子結點,相反也成立
6 堆排序
算法思想:先將數列進行最大堆初始化,造成一個最大堆,而後將最大堆的頂部數與最後面的待交換數進行交換,交換後再進行最大堆調整,調整後最大的數又到了頂部,再交換,一直作這樣的操做直到交換到根部時中止。
最大堆初始化:首先咱們要理清一些關係,假設父節點是序數是i,則左子節點是2i+1,右子節點時2i+2,而假設子節點是i,則父節點是(i-1)/2;最大堆的概念是節點要大於等於左右子節點的值;調整是從第一個非葉子節點開始調整,調用「調整最大堆」函數進行調整,思想能夠看「調整最大堆」,直到調整到根節點即表示初始化完最大堆了。
調整最大堆:好比要調整的是序數i,則跟它的左右子節點2i+1和2i+2進行比較,若是左右子節點的值有大於該節點,則進行交換,而後再用該替換的子節點從新做爲調整序號,跟它的左右子節點值比較,直到沒有交換就中止
python代碼實現:
def swap(num, num_a, num_b): tmp = num[num_a] num[num_a] = num[num_b] num[num_b] = tmp def max_heap_down(num, cur, last): max = cur while True: if 2*cur+1 <= last and num[2*cur+1] > num[max]: max = 2*cur+1 if 2*cur+2 <= last and num[2*cur+2] > num[max]: max = 2*cur+2 if max != cur: swap(num, cur, max) cur = max else: return def initial_max_heap(num, num_len): for i in range((num_len-1)/2, -1, -1): max_heap_down(num, i, num_len-1) def heap_sort(num): if not num or len(num) <= 1: return num_len = len(num) initial_max_heap(num, num_len) swap(num, 0, num_len-1) for i in range(num_len-2, 0, -1): max_heap_down(num, 0, i) swap(num, 0, i)
算法時間複雜度:O(nlgn)
算法空間複雜度:O(1)
算法穩定性:不穩定
該算法的算法複雜度能夠這樣理解:max_heap_down該函數調整最大堆的複雜度是O(lgn),而初始化最大堆會調用非葉子節點次,設非葉子節點個數爲x,則x<n,因此初始化最大堆時間複雜度是xlgn;開始交換數據時,一共要交換n-2次,每次會涉及到一次max_heap_down調用,因此這裏時間複雜度是(n-2)lgn,因此總的時間複雜度是nlgn
7 計數排序
適用於某些有特性的數列,好比某段範圍內的正整數排序,固然負數也能夠,作下映射就能夠了
算法思想:因爲是正整數,是有範圍的,設最大爲m,因此能夠開闢一個長度爲m+1的數列,用來記錄某個正整數有幾個,而後遍歷一遍要排序的數列,把對應數字的個數記錄下來,再經過記錄下來的數字,複寫到原數列中。
python代碼實現:
def counting_sort(num, m): if not num or len(num) <= 1: return num_len = len(num) tmp_num = [0] * (m+1) for i in range(0, num_len): tmp_num[num[i]] += 1 tmp = 0 for i in range(0, m+1): if tmp >= num_len: break while tmp_num[i] > 0: num[tmp] = i tmp_num[i] -= 1 tmp += 1 num = [3, 5, 1, 7, 2, 8, 4, 100, 76, 78]
算法時間複雜度:O(n+m)
算法空間複雜度:O(m)
算法穩定性:穩定
8 桶排序
算法思想:把一段區間分紅n等分,好比有100個數字,設置10個桶,則1-10就會在第1個桶,11-20就會在第2個桶,以此類推,而後遍歷一遍數組,將對應的數字放到對應的桶中,而後分別對每一個桶的隊列進行排序,最後從新賦值到原數組中。這裏比較關鍵的是怎麼劃分區域,怎麼經過計算就能夠獲得某個數值對應哪一個桶,這樣理解會更簡單點,把一個數值當作是一個蘋果,首先肯定一共有多少個蘋果,而後選定一個桶的數量,我這裏的算法是若是蘋果數量少於10個,則桶的數量等於蘋果的數量,不然桶的數量設置爲10,首先用蘋果數量除以桶來保證每一個桶平均能分配到多少個,獲得avg_num值,剩餘的就都給最後一個桶。這樣咱們肯定哪一個值屬於哪一個桶時就很好肯定,直接用該數值減去最小值而後除以avg_num便可,若是獲得的大於桶的數量,則視爲最後一個桶。都放進各個桶後,分別對各個桶排序,這裏使用了快速排序來對各個桶進行排序。
python代碼實現:
def bucket_sort(num): if not num or len(num) <= 1: return num_len = len(num) max_num = max(num) min_num = min(num) bucket_num = 10 if (max_num - min_num + 1) < 10: bucket_num = max_num - min_num avg_num = (max_num - min_num) / bucket_num tmp_arr = [[] for i in range(bucket_num)] for i in range(num_len): bucket_pos = (num[i] - min_num + 1) / avg_num if bucket_pos >= bucket_num: bucket_pos = bucket_num - 1 tmp_arr[bucket_pos].append(num[i]) tmp = 0 for i in range(bucket_num): quick_sort(tmp_arr[i]) for j in tmp_arr[i]: num[tmp] = j tmp += 1
算法時間複雜度:有n個數字,設有m個桶,每一個桶有k個數,O(n) + O(m) * klgk,當桶足夠多,每一個桶只有1個數字時,就變成O(n + m)
算法空間複雜度:O(n+m)
算法穩定性:取決於在對每一個桶進行排序使用的是什麼排序算法,若是是快速排序則是不穩定的,若是是冒泡排序則是穩定的
9 基數排序
算法思想:基數排序(radix sorting)將全部待比較數值(正整數)統一爲一樣的數位長度,數位較短的數前面補零。 而後從最低位開始,依次進行一次排序。這樣從最低位排序一直到最高位排序完成之後, 數列就變成一個有序序列。
用的比較少,這裏就不實現了
算法時間複雜度:O(n)
算法空間複雜度:O(n)
算法穩定性:穩定
10 希爾排序
算法思想:希爾排序是借鑑了插入排序在基本有序下是很是高效的特色,因此希爾排序會先設定個step(咱們這裏選了數列長度的一半),每隔step個數字,組成一個要排序的數列進行排序,這樣就會有多個數列進行插入排序,排完一輪後,step除於2,而後再組建數列再插入排序,直到step變爲1後就是整個數列再進行插入排序一次,而後終止。該算法會比插入排序好的緣由是由於它提早對讓數值順序儘快接近有序,發揮插入排序在基本有序狀況下高效的特色。
python代碼實現:
def shell_sort(num): if not num or len(num) <= 1: return num_len = len(num) step = num_len / 2 while step > 0: for i in range(step): for j in range(i+step, num_len, step): for k in range(i, j, step): if num[j] < num[k]: break else: k += step if k < j: tmp_k = k tmp = num[j] while tmp_k >= k: x = num[tmp_k] num[tmp_k] = num[tmp_k+step] num[tmp_k+step] = x tmp_k -= step num[k] = tmp step /= 2
算法時間複雜度:比插入排序好,某些狀況下可達到O(n*1.3)算法空間複雜度:O(1)算法穩定性:穩定