不管是任何程序員,不管是算法,仍是其餘,都須要掌握必定的數據結構。本文以最優雅的方式,基於Python,完成算法,不要問,背下來就好。代碼量更少,更好背。python
第1篇 查找和排序:二分查找、冒泡排序、選擇排序、插入排序、希爾排序、歸併排序、快速排序。程序員
二分查找,時間複雜度O(logn),排序一次,查找屢次,排序成本能夠忽略;只查找一次,則順序查找比較好。非遞歸12行,遞歸10行。github
# 源碼: https://github.com/SpikeKing/data_structure_with_python
def binary_search(alist, item):
""" 二分查找,非遞歸 1. 2個參數,待查找alist和查找項item 2. 聲明2個變量,first,last 3. while條件,first小於等於last 4. mid是first和last的中值(整除); 5. 三個if條件,相等alist[mid]=item,小於中值換last,大於中值換first; 6. 默認返回False,12行 :param alist: 待查找alist :param item: 待查找項item :return: 是否找到 """
first = 0
last = len(alist) - 1
while first <= last:
mid = (first + last) // 2
if alist[mid] == item:
return True
else:
if alist[mid] > item:
last = mid - 1
else:
first = mid + 1
return False
def binary_search_re(alist, item):
""" 二分查找,遞歸 1. if終止條件,長度爲0,返回False; 2. 中點是長度除2,中值判斷; 3. 小於遞歸前半部分,大於遞歸後半部分,返回遞歸函數; 4. 10行 :param alist: 待查找alist :param item: 待查找項item :return: 是否找到 """
if len(alist) == 0:
return False
mid = len(alist) // 2
if alist[mid] == item:
return True
else:
if alist[mid] > item:
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()
複製代碼
冒泡排序,時間複雜度O(n^2),冒泡排序一般被認爲是低效的排序方式。優點是:識別排序列表,和提早終止排序。短冒泡排序就是提早終止的冒泡排序。冒泡排序4行,短冒泡排序8行。算法
# 源碼: https://github.com/SpikeKing/data_structure_with_python
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 bubble_sort_short(alist):
""" 短冒泡排序,增長exchange,額外終止參數 1. 初始爲True,當爲False時終止; 2. 在第2次循環前,設置爲False,交換一次就設置爲True,一次未交換則觸發終止; 3. 8行,增長5行的exchange操做 :param alist: :return: """
for p_num in range(len(alist) - 1, 0, -1):
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
if not exchange:
# print('提早終止')
return
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()
複製代碼
選擇排序,時間複雜度O(n^2),比較次數與冒泡排序相同,交換次數小於冒泡排序。 選擇排序6行。shell
# 源碼: https://github.com/SpikeKing/data_structure_with_python
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 i in range(1, p_num + 1):
if alist[i] > alist[max_loc]:
max_loc = i
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()
複製代碼
插入排序,時間複雜度O(n^2),用移位代替交換,移位操做的時間大約是交換時間的1/3。插入排序7行。數據結構
# 源碼: https://github.com/SpikeKing/data_structure_with_python
def insert_sort(alist):
""" 插入排序,子序列逐漸有序 1. 遍歷列表,存儲當前值cur_val,設置遊標pos 2. 遊標大於0和遊標的值大於當前值,則移位,同時遊標減1; 3. 將當前值賦予遊標的終止位置; 4. 7行 :param alist: 待排序alist :return: None """
for i in range(1, len(alist)):
cur_val = alist[i]
pos = i # 遊標
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()
複製代碼
希爾排序,時間複雜度,介於O(n)~O(n^2),也能夠認爲是O(n^3/2),插入排序的改進,比較和移位較少,每次遍歷都會生成一個"更有序"的子列表。12行,增量部分5行,插入部分7行。函數
# 源碼: https://github.com/SpikeKing/data_structure_with_python
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):
insert_sort_gap(alist, s_pos, gap)
gap = gap // 2
def insert_sort_gap(alist, s_pos, gap):
""" 帶增量的插入排序 :param alist: 待排序alist :param s_pos: 起始位置 :param gap: 增量 :return: None """
for i in range(s_pos + gap, len(alist), gap):
cur_val = alist[i]
pos = i
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()
複製代碼
歸併排序,時間複雜度O(nlogn)。20行。ui
# 源碼: https://github.com/SpikeKing/data_structure_with_python
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 = alist[:mid]
right = alist[mid:]
merge_sort(left)
merge_sort(right)
i, j, k = 0, 0, 0
while i < len(left) and j < len(right):
if left[i] < right[j]:
alist[k] = left[i]
i += 1
else:
alist[k] = right[j]
j += 1
k += 1
if i < len(left):
alist[k:] = left[i:]
if j < len(right):
alist[k:] = right[j:]
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()
複製代碼
快速排序,時間複雜度O(nlogn)。不須要額外空間14行,須要額外空間7行。spa
# 源碼: https://github.com/SpikeKing/data_structure_with_python
def quick_sort(alist, fst, lst):
""" 快速排序 1. 肯定終止條件,起始大於等於終止; 2. 起始fst和終止lst的位置,樞軸值pivot是第1個值; 3. 遍歷i和j,i是第2個,j是最後一個; 4. 循環交換,直到i和j交叉; 5. 樞軸索引取i和j最小的1個; 6. 交換樞軸位置的值與起始位置的值; 7. 遞歸調用左右兩部分; 8. 16行 :param alist: 待排序列表 :param fst: 起始idx :param lst: 終止idx :return: None """
if fst >= lst:
return
pivot = alist[fst]
i, j = fst + 1, lst
# bugfix: 增長等號,用於移動中心軸位置,最後交換使用 @Thx lovemoon
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
p_idx = min(i, j) # 樞軸索引
alist[fst], alist[p_idx] = alist[p_idx], alist[fst]
quick_sort(alist, fst, p_idx - 1)
quick_sort(alist, p_idx + 1, lst)
def quick_sort_v2(alist):
""" 快速排序,須要額外空間 :param alist: 待排序列表 :return: 排序列表 """
if len(alist) <= 1:
return alist
pivot = alist[0]
small = [i for i in alist if i < pivot]
mid = [i for i in alist if i == pivot]
large = [i for i in alist if i > pivot]
return quick_sort_v2(small) + mid + quick_sort_v2(large)
複製代碼
OK, that's all! Enjoy it!