1 冒泡排序
算法思想:從第一個開始,相鄰兩個數進行比較,如果前1個數大於后一個數,則進行調換,這樣換到最后,最大的那個就會在最后面,重復這個過程,較大的就會逐個累積在后面,本質思想就是大的在一輪中會逐漸冒泡到后排。
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代碼實現:
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代碼實現:
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代碼遞歸實現:
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²,在要排序的數一開始是有序的就會形成這樣的結果。
兩頭交換思想:選取一個中間的值作為基准,然后左邊找一個大於等於該基准的值,右邊找一個小於等於該基准的值,找到后如果左邊序號小於等於右邊序號則進行交換,並左邊序號+1,右邊序號-1,直到左邊序號大於右邊序號時退出,這樣最后也是以基准值為區分把小於等於基准值的放到左邊,大於等於基准值的放到右邊,然后再分別遞歸。注意while start_tmp <= end_tmp這里要取<=號,因為下面分別遞歸調用時才不會有重疊,也就是左邊的start_tmp和右邊的end_tmp不要相等,要讓它們再互相走多一步。
python代碼實現:
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)
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)
算法穩定性:穩定