算法(Algorithm):一個計算過程,解決問題的方法。算法
輸入→算法→輸出編程
時間複雜度:用來評估算法運行效率的一個東西。數據結構
print('Hello World') #假如說這行代碼運行時間是一個單位O(1)
for i in range(n): # 這段代碼的時間是O(n),由於執行了n次 print('Hello World')
for i in range(n): # 這段代碼是O(n*n),由於在執行了n*n次 for j in range(n): print('Hello World')
for i in range(n): #這代碼是O(n*n*n),執行了n的立方次 for j in range(n): for k in range(n): print('Hello World')
小結:app
時間複雜度是用來估計算法運行時間的一個式子(單位)。 通常來講,時間複雜度高的算法比複雜度低的算法慢。 常見的時間複雜度(按效率排序): O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n2logn)<O(n3) 不常見的時間複雜度(看看就好) O(n!) O(2n) O(nn) …
空間複雜度:用來評估算法內存佔用大小的一個式子優化
空間換時間:分給它一些空間或內存,讓它運行速度更快ui
遞歸的兩個特色:spa
1.調用自身3d
2.有結束條件code
def func(x): if x>0: print(x) func(x-1) print(4) # 打印結果 4 3 2 1 #由於先打印再遞歸
def func(x): if x > 0: func(x-1) print(x) func(4) # 打印結果 1 2 3 4 # 由於先遞歸,再打印
打印 抱着抱着抱着抱着抱着個人小鯉魚的個人個人個人個人我blog
def test(n): if n == 0: print("個人小鯉魚", end='') else: print("抱着", end='') test(n-1) print("的我", end='') test(5) # 尾遞歸
漢諾塔問題
t = 0 def hanoi(n, A, B, C): global t if n > 0: hanoi(n-1, A, C, B) t += 1 print("%s -> %s" % (A, C)) hanoi(n-1, B, A, C) # hanoi(8,'A','B','C') # 8表示8層 A B C 參數不能變 # print(t)
列表查找:從列表中查找指定元素
輸入:列表、待查找元素
輸出:元素下標或未查找到元素
順序查找:從列表第一個元素開始,順序進行搜索,直到找到爲止。
二分查找:從有序列表的候選區data[0:n]開始,經過對待查找的值與候選區中間值的比較,可使候選區減小一半。
有序列表,列表的元素值隨着索引值的增長而增長:
def bin_search(li, val): # li是傳入的列表 val是要查找的值 low = 0 # low是起始的索引值 high = len(li) - 1 # high是末尾的索引值 while low <= high: # 知足起始索引小於末尾索引的條件就執行循環 mid = (low + high) // 2 # mid是列表的中間數的索引 if li[mid] == val: # 正好找到要查找的值的索引 return mid elif li[mid] < val: # 中間數的值小於被查找的值 low = mid + 1 # 說明val在中間數的右邊 else: high = mid - 1 # 說明val在中間數的左邊
def bin_search_rec(data_set, value, low, high): if low <= high: mid = (low + high) // 2 if data_set[mid] == value: return mid elif data_set[mid] > value: return bin_search_rec(data_set, value, low, mid - 1) else: return bin_search_rec(data_set, value, mid + 1, high) else: return
列表排序:將無序列表變成有序列表
輸入:無序列表
輸出:有序列表
順序:升序與倒序
排序low B 三人組;
冒泡排序 其次最多 O(n*n)
選擇排序 O(n*n)
插入排序
排序niu B 三人組:
快速排序 用得最多
堆排序 最難的
歸併排序
冒泡排序:兩層遍歷,相鄰的兩個值,若是左邊的數大於右邊的數,則交換位置。
li = [1, 2, 1, 3, 1, 4, 5, 4, 6, 7, 6, 8, 4, 5, 3, 2]
def bubble_sort(li):
for i in range(len(li)-1, 0, -1):
for k in range(i):
if li[k] > li[k+1]:
li[k], li[k+1] = li[k+1], li[k]
return li
print(bubble_sort(li))
選擇排序:遍歷一趟記錄最小的數,放到第一位。再遍歷剩下的數,找的最小的數,繼續放置。
關鍵點:無序區和最小數的位置。
li = [1, 2, 1, 3, 1, 4, 5, 4, 6, 7, 6, 8, 4, 5, 3, 2]
def select_sort(li):
for i in range(len(li) - 1): # 總趟數能夠是len(li)次,也能夠是len(li)-1次,由於倒數第二趟結束的時候,列表已經排序完成。
# i表示趟數,也表示無序區的第一個數 min_loc = i # for j in range(i+1, len(li)): if li[j] < li[min_loc]: # 每趟都把最小的數放到無序區第一個位置 min_loc = j li[min_loc], li[i] = li[i], li[min_loc] # 有序區會多一個數,無序區會少一個數。接下來下一趟開始 return li print(select_sort(li))
列表被分爲有序區和無序區兩個部分。最初有序區只有一個元素。
每次從無序區選擇一個元素,插入到有序區的位置,直到無序區變空。
代碼關鍵點:如何找到無序區數,如何插到有序區中。
li = [1, 2, 1, 3, 1, 4, 5, 4, 6, 7, 6, 8, 4, 5, 3, 2]
def insert_sort(li):
for i in range(1,len(li)): # 無序區的範圍,第一次無序區只有列表的第一個數,因此無序區範圍就是range(1,len(li))
# i表示摸到的牌的位置
tem = li[i]
j = i - 1 # j表示有序區的最後面的一個數,j也表示有序區用來和i進小紅比較的數
# 若是有序區的這個用來比較的值比tem大,則用索引爲j-1的值繼續比較.
# 若是tem這個值比有序區的全部數都小,這是索引是-1,表示找到位置,並退出while循環並插入
while j >= 0 and li[j] > tem:
li[j+1], li[j] = li[j], li[j+1] # 把tem的值往j的位置移
j -= 1 # 往前看
# 若是有序區的數比tem小,表示找到位置,就插入
li[j+1] = tem
return li
print(insert_sort(li))
插入排序優化:應用二分查找來尋找插入點(並無什麼卵用)
快速排序:取一個元素p(第一個元素),使元素p歸位,列表被p元素分爲兩部分,左邊都比p小,右邊都比p大,而後遞歸完成排序。
li = [1, 2, 1, 3, 1, 4, 5, 4, 6, 7, 6, 8, 4, 5, 3, 2]
def partition(li, left, right):
tem = li[left] # 第一次取第一個元素left = 0
while left < right: # 列表裏至少兩個元素才知足這個條件
while left < right and li[right] >= tem: # 從右邊邊找小於tem的數
right -= 1 # 若是不小於,繼續找
li[left], li[right] = li[right], li[left] # 找到比tem小的數,挪到左邊
while left < right and li[left] <= tem: # 從左邊找比tem大的數
left += 1
li[right], li[left] = li[left], li[right]
li[left] = tem
return left # 這裏返回left和right是同樣的
def quick_sort(li, left, right):
if left < right: # 列表裏至少兩個元素才知足這個條件
mid = partition(li, left, right)
quick_sort(li, left, mid-1) # 遞歸
quick_sort(li, mid+1, right) # 遞歸
return 111
quick_sort(li, 0, len(li)-1)
print(li)
快排的最壞狀況:列表是倒序的[9,8,7,6,5,4,3,2,1,0]
樹是一種數據結構 好比:目錄結構
樹是一種能夠遞歸定義的數據結構
樹是由n個節點組成的集合:
若是n=0,那這是一棵空樹;
若是n>0,那存在1個節點做爲樹的根節點,其餘節點能夠分爲m個集合,每一個集合自己又是一棵樹。
一些概念
根節點:沒有父節點的是根節點
葉子節點:沒有子節點的是葉子節點
樹的深度(高度):幾層就是幾
樹的度:就是他有幾個節點(A有6個節點,度就是6。j的是2。f的是3。整個樹的度,就是最大的度。)
孩子節點/父節點:A是B的父節點,B是A的孩子節點
子樹:任何一個節點和它的孩子節點就是一個子樹,沒有孩子節點他也是一個樹。
二叉樹:度不超過2的樹(節點最多有兩個叉)
B是A的左孩子,C是A的右孩子
滿二叉樹:一個二叉樹,若是每個層的結點數都達到最大值,則這個二叉樹就是滿二叉樹。
徹底二叉樹:葉節點只能出如今最下層和次下層,而且最下面一層的結點都集中在該層最左邊的若干位置的二叉樹。
二叉樹的存儲方式
鏈式存儲方式
順序存儲方式(列表)
大根堆:一棵徹底二叉樹,知足任一節點都比其孩子節點大
小根堆:一棵徹底二叉樹,知足任一節點都比其孩子節點小
堆未完待續。。。。。。
假設如今的列表分兩段有序,如何將其合成爲一個有序列表
分別從兩個有序的片斷取值,按從小到大的順序取值。先比較兩個片斷的最小值,誰小就取出來。
因此順序是1-2-3-4-5-6,將6取出來後,右邊的片斷沒值了,就將左邊剩下的片斷一次性取出來7-8-9。
而後就組合成一個新的有序列表了。
這種操做稱爲一次歸併
def merge(li, low, mid, high): # high是右邊片斷最後一個數的索引 li_tem = [] i = low # low是左邊片斷第一個數的索引 j = mid + 1 # mid是左邊片斷最大的數的索引 while i <= mid and j <= high: # 若是左右兩個片斷都有值,就繼續取值。只要其中一個片斷沒值,就跳出循環。 if li[i] < li[j]: # 若是左邊片斷的最小值小於右邊的最小值 li_tem.append(li[i]) # 就將最小的那個值取出來放到一個列表中。 i += 1 # 而後繼續比較剩下的最小值 else: # 反之,就是右邊的最小值小於左邊的最小值 li_tem.append(li[j]) j += 1 # 而後繼續比較剩下的最小值 # 跳出第一個while循環的條件是:若是左邊的片斷取完了,其索引i不小於mid了 # 若是右邊的片斷取完了,其索引j就不小於high了 # 下面的兩個while循環只可能有一個執行 while i <= mid: # 這裏是左偏片斷還有值 li_tem.append(li[i]) i += 1 # 繼續添加到列表中 while j <= high: # 這裏是右邊片斷還有值 li_tem.append(li[j]) j += 1 # 繼續添加到列表中 li[low:high+1] = li_tem # 將li_tem copy給li li = [2, 5, 7, 8, 9, 1, 3, 4, 6] merge(li, 0, 4, len(li)-1) print(li)
若是不是兩端有序列表怎麼用歸併,加上遞歸就能夠了。
先把列表分解,而後合併。下半段的每一步操做都是一個歸併。
歸併排序
def merge(li, low, mid, high): # high是右邊片斷最後一個數的索引 li_tem = [] i = low # low是左邊片斷第一個數的索引 j = mid + 1 # mid是左邊片斷最大的數的索引 while i <= mid and j <= high: # 若是左右兩個片斷都有值,就繼續取值。只要其中一個片斷沒值,就跳出循環。 if li[i] < li[j]: # 若是左邊片斷的最小值小於右邊的最小值 li_tem.append(li[i]) # 就將最小的那個值取出來放到一個列表中。 i += 1 # 而後繼續比較剩下的最小值 else: # 反之,就是右邊的最小值小於左邊的最小值 li_tem.append(li[j]) j += 1 # 而後繼續比較剩下的最小值 # 跳出第一個while循環的條件是:若是左邊的片斷取完了,其索引i不小於mid了 # 若是右邊的片斷取完了,其索引j就不小於high了 # 下面的兩個while循環只可能有一個執行 while i <= mid: # 這裏是左偏片斷還有值 li_tem.append(li[i]) i += 1 # 繼續添加到列表中 while j <= high: # 這裏是右邊片斷還有值 li_tem.append(li[j]) j += 1 # 繼續添加到列表中 li[low:high+1] = li_tem def merge_sort(li, low, high): if low < high: # 保證列表至少有兩個元素 mid = (low + high) // 2 # 取中間數的索引 merge_sort(li, low, mid) # 將左邊的經過遞歸編程有序列表 merge_sort(li, mid+1, high) # 將右邊的經過遞歸編程有序列表 merge(li, low, mid, high) # 將左右兩個片斷合併成一個有序列表 li = [2, 5, 7, 8, 9, 1, 3, 4, 6, 10] merge_sort(li, 0, len(li)-1) print(li)
三種排序算法的時間複雜度都是O(nlogn)
通常狀況下,就運行時間而言:
快速排序<歸併排序<堆排序
三種排序算法的缺點:
快速排序:極端狀況下,排序效率低
歸併排序:須要額外的內存開銷
堆排序:在快的排序算法中相對較慢
希爾排序 待續。。。。。。
計數排序 待續。。。。。。
桶排序 待續。。。。。。
基數排序 待續。。。。。。