程式設計師必須掌握的資料結構 1
無論是任何程式員,不論是演算法,還是其他,都需要掌握一定的資料結構。本文以最優雅的方式,基於Python,完成演算法,不要問,背下來就好。程式碼量更少,更好背。
ofollow,noindex">原始碼 :github.com/SpikeKing/d…
第1篇 查詢和排序:二分查詢、氣泡排序、選擇排序、插入排序、希爾排序、歸併排序、快速排序。
1. 二分查詢
二分查詢,時間複雜度O(logn),排序一次,查詢多次,排序成本可以忽略;只查詢一次,則順序查詢比較好。非遞迴13行,遞迴11行。
def binary_search(alist, item): """ 二分查詢,非遞迴 1. 2個引數,待查詢alist和查詢項item 2. 宣告3個變數,first,last,found(返回值) 3. while條件,first小於等於last,found是false 4. mid是first和last的中值(整除); 5. 三個if條件,相等alist[mid]=item,小於中值換last,大於中值換first; 6. 返回found,13行 :param alist: 待查詢alist :param item: 待查詢項item :return: 是否找到 """ first = 0 last = len(alist) - 1 found = False while first <= last and not found: mid = (first + last) // 2 if alist[mid] == item: return True else: if item < alist[mid]: last = mid - 1 else: first = mid + 1 return found def binary_search_re(alist, item): """ 二分查詢,遞迴 1. if終止條件,長度為0,返回False; 2. 中點是長度除2,中值判斷; 3. 小於遞迴前半部分,大於遞迴後半部分,返回遞迴函式; 4. 11行 :param alist: 待查詢alist :param item: 待查詢項item :return: 是否找到 """ if len(alist) == 0: return False else: mid = len(alist) // 2 if alist[mid] == item: return True else: if item < alist[mid]: return binary_search_re(alist[:mid], item) else: return binary_search_re(alist[mid + 1:], item) def test_of_binary_search(): test_list = [0, 1, 2, 8, 13, 17, 19, 32, 42] print(binary_search(test_list, 3)) print(binary_search(test_list, 13)) print(binary_search_re(test_list, 3)) print(binary_search_re(test_list, 13)) if __name__ == '__main__': test_of_binary_search() 複製程式碼
2. 氣泡排序
氣泡排序,時間複雜度O(n^2),氣泡排序通常被認為是低效的排序方式。優勢是:識別排序列表,和提前終止排序。氣泡排序4行,短氣泡排序9行。
def bubble_sort(alist): """ 氣泡排序 1. 兩次遍歷,第1次遍歷長度,倒序逐漸減1,每遍歷一次,排序一個; 2. 第2次,正常遍歷,少1個值,因為i和i+1; 3. 當前位大於下一位,交換當前位和下一位; 4. 4行 :param alist: 待排序列表 :return: None,內部排序 """ for p_num in range(len(alist) - 1, 0, -1): for i in range(p_num): if alist[i] > alist[i + 1]: alist[i], alist[i + 1] = alist[i + 1], alist[i] def short_bubble_sort(alist): """ 短氣泡排序,增加exchange,額外終止引數 1. 初始為True,當為False時終止; 2. 在第2次迴圈前,設定為False,交換一次就設定為True,一次未交換則觸發終止; 3. 9行,增加5行的exchange操作 :param alist: :return: """ exchange = True for p_num in range(len(alist) - 1, 0, -1): if not exchange: break exchange = False for i in range(p_num): if alist[i] > alist[i + 1]: alist[i], alist[i + 1] = alist[i + 1], alist[i] exchange = True def test_of_bubble_sort(): alist = [54, 26, 93, 17, 77, 31, 44, 55, 20] # bubble_sort(alist) # print(alist) alist = [17, 20, 26, 93, 77, 31, 44, 55, 54] short_bubble_sort(alist) print(alist) if __name__ == '__main__': test_of_bubble_sort() 複製程式碼
3. 選擇排序
選擇排序,時間複雜度O(n^2),比較次數與氣泡排序相同,交換次數小於氣泡排序。 選擇排序6行。
def selection_sort(alist): """ 選擇排序,即選擇最大值再交換 1. 依然是2次遍歷,第1次反序,第2次正序,注意起始為1,末尾+1; 2. max_loc儲存最大值,預設第0位; 3. 當loc的值大於max_loc的值時,max_loc重新賦值; 4. 交換loc和max_loc 5. 6行 :param alist: 待排序alist :return: None """ for p_num in range(len(alist) - 1, 0, -1): max_loc = 0 for loc in range(1, p_num + 1): if alist[loc] > alist[max_loc]: max_loc = loc alist[p_num], alist[max_loc] = alist[max_loc], alist[p_num] def tes_of_selection_sort(): alist = [54, 26, 93, 17, 77, 31, 44, 55, 20] selection_sort(alist) print(alist) if __name__ == '__main__': tes_of_selection_sort() 複製程式碼
4. 插入排序
插入排序,時間複雜度O(n^2),用移位代替交換,移位操作的時間大約是交換時間的1/3。插入排序7行。
def insert_sort(alist): """ 插入排序, 1. 遍歷列表,儲存當前值cur_val,設定遊標pos 2. 遊標大於0和遊標的值大於當前值,則移位,同時遊標減1; 3. 將當前值賦予遊標的終止位置; 4. 7行 :param alist: 待排序alist :return: None """ for idx in range(1, len(alist)): cur_val = alist[idx] pos = idx# 遊標 while pos > 0 and alist[pos - 1] > cur_val: alist[pos] = alist[pos - 1] pos -= 1 alist[pos] = cur_val# 最後遊標的位置 def test_of_insert_sort(): alist = [54, 26, 93, 17, 77, 31, 44, 55, 20] insert_sort(alist) print(alist) if __name__ == '__main__': test_of_insert_sort() 複製程式碼
5. 希爾排序
希爾排序,時間複雜度,介於O(n)~O(n^2),也可以認為是O(n^3/2),插入排序的改進,比較和移位較少,每次遍歷都會生成一個"更有序"的子列表。12行,增量部分5行,插入部分7行。
def shell_sort(alist): """ 希爾排序 1. 兩部分,第1部分,計算增量gap和起始位置s_pos; 2. 增量是累除2,s_pos是增量的遍歷 3. 增量插入排序,額外傳入起始位置和增量; 4. range的起始由起始位置+增量; 5. 迴圈條件為大於等於增量,差值為增量 6. 12行,增量部分5行,插入部分7行 :param alist: 待排序alist :return: None """ gap = len(alist) // 2 while gap > 0: for s_pos in range(gap): gap_insert_sort(alist, s_pos, gap) gap = gap // 2 def gap_insert_sort(alist, s_pos, gap): """ 帶增量的插入排序 :param alist: 待排序alist :param s_pos: 起始位置 :param gap: 增量 :return: None """ for idx in range(s_pos + gap, len(alist), gap): cur_val = alist[idx] pos = idx while pos >= gap and alist[pos - gap] > cur_val: alist[pos] = alist[pos - gap] pos -= gap alist[pos] = cur_val def test_of_shell_sort(): alist = [54, 26, 93, 17, 77, 31, 44, 55, 20] shell_sort(alist) print(alist) if __name__ == '__main__': test_of_shell_sort() 複製程式碼
6. 歸併排序
歸併排序,時間複雜度O(nlogn)。20行。
def merge_sort(alist): """ 歸併排序 1. 遞迴,結束條件,只有1個元素,return; 2. mid中心,左右兩部分,遞迴呼叫merge_sort; 3. 遍歷左右,新增較小的值;遍歷其餘部分; 4. 20行 :param alist: :return: """ if len(alist) < 2: return mid = len(alist) // 2 left_half = alist[:mid] right_half = alist[mid:] merge_sort(left_half) merge_sort(right_half) i, j, k = 0, 0, 0 while i < len(left_half) and j < len(right_half): if left_half[i] < right_half[j]: alist[k] = left_half[i] i += 1 else: alist[k] = right_half[j] j += 1 k += 1 while i < len(left_half): alist[k:] = left_half[i:] while j < len(right_half): alist[k:] = right_half[i:] def test_of_merge_sort(): alist = [54, 26, 93, 17, 77, 31, 44, 55, 20] merge_sort(alist) print(alist) if __name__ == '__main__': test_of_merge_sort() 複製程式碼
7. 快速排序
快速排序,時間複雜度O(nlogn)。不需要額外空間14行,需要額外空間7行。
def quick_sort(alist, fst, lst): """ 快速排序 1. 確定終止條件,起始等於終止; 2. 起始fst和終止lst的位置,樞軸的值pivot; 3. 遍歷i和j, :param alist: 待排序列表 :param fst: 起始idx :param lst: 終止idx :return: None """ if fst >= lst: return i, j = fst, lst pivot = alist[fst] while i <= j: while alist[i] < pivot: i += 1 while alist[j] > pivot: j -= 1 if i <= j: alist[i], alist[j] = alist[j], alist[i] i, j = i + 1, j - 1 quick_sort(alist, fst, j) quick_sort(alist, i, lst) def quick_sort_v2(alist): """ 快速排序 :param alist: :return: """ if len(alist) < 2: return alist pivot = alist[0] small = [i for i in alist if i < pivot] medium = [i for i in alist if i == pivot] large = [i for i in alist if i > pivot] return quick_sort_v2(small) + medium + quick_sort_v2(large) def test_of_quick_sort(): alist = [54, 26, 93, 17, 77, 31, 44, 55, 20] # quick_sort(alist, 0, len(alist) - 1) # print(alist) alist = quick_sort_v2(alist) print(alist) if __name__ == '__main__': test_of_quick_sort() 複製程式碼
OK, that's all! Enjoy it!