算法、數據結構、金融 算法ppt ,72頁!
day30 算法(algorithm):11個py文件 (查找和排序)
1.簡介--算法必考
一個計算過程,解決問題的方法
時間複雜度:用來估算算法運行時間的一個式子(單位),通常來講,時間
複雜度高的算法比複雜度低的算法慢。
空間複雜度:用來評估算法內存佔用大小的一個式子----通常不討論了
時間複雜度小結:
時間複雜度是用來估計算法運行時間的一個式子(單位)。 通常來講,時間複雜度高的算法比複雜度低的算法慢。 常見的時間複雜度(按效率排序) O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n2logn)<O(n3) 不常見的時間複雜度(看看就好) O(n!) O(2n) O(nn) … 如何一眼判斷時間複雜度? 循環減半的過程O(logn) 幾回循環就是n的幾回方的複雜度
遞歸:調用自身,結束條件
# 斐波那契數列
# 1 1 2 3 5 8 13
# 1 2 3 4 5 # def func(x): # if x>0: # func(x-1) # print(x) # # func(5) # 斐波那契數列 # 1 1 2 3 5 8 13 # def fib(n): # if n == 0 or n == 1: # return 1 # else: # return fib(n-1) + fib(n-2) # print(fib(100)) # def fib(n): # li = [1,1] # for i in range(2,n+1): # li.append(li[-1]+li[-2]) # return li[-1] # print(fib(100)) def fib(n): if n == 0 or n == 1: return 1 a = 1 b = 1 c = 1 for i in range(2, n + 1): a = b b = c c = a + b return c print(fib(100))
漢諾塔問題:移盤子
def hanoi(n, A, B, C): if n > 0: hanoi(n-1, A, C, B) print('%s->%s' % (A, C)) hanoi(n-1, B, A, C) hanoi(5, 'A', 'B', 'C')
走樓梯,遞歸問題,相似於斐波那契,按照最後一步是一階或者兩階去算
鋪磚問題同理
列表查找:順序查找、二分查找(須要是有序列表)
第21頁ppt兩個查找代碼
def linear_search(data_set, value): for i in range(range(data_set)): if data_set[i] == value: return i return 時間複雜度:O(n) def bin_search(li, val): low = 0 high = len(li) - 1 while low <= high: mid = (low + high) // 2 if li[mid] > val: high = mid - 1 elif li[mid] < val: low = mid + 1 else: # = return mid return None 時間複雜度:O(logn) 應用: li = [1,3,4,5,6,8,9,80] print(bin_search(li,5))
遞歸版二分查找
列表是對象,能夠按照key去查
列表排序:冒泡,選擇,插入;快速排序,堆排序,歸併排序
算法關鍵點:有序區、無序區
1.冒泡:必需要會
一趟以後,最大的元素在最上面,共須要n-1趟
順序: [0,n-i-1]
箭頭到n-i-2,但前包後不包,for循環寫n-i-1
import random from timewrap import cal_time @cal_time # def bubble_sort(li): # for i in range(len(li)-1): # i表示第i趟 # for j in range(len(li)-i-1): # j表示箭頭位置, # if li[j] > li[j+1]: # li[j], li[j+1] = li[j+1], li[j] # print(li) # li = list(range(10)) # random.shuffle(li) #將函數打亂 # print(li) #這10個數開始的順序 # bubble_sort(li) @cal_time def bubble_sort_2(li): for i in range(len(li)-1): # i表示第i趟 exchange = False for j in range(len(li)-i-1): # j表示箭頭位置 if li[j] > li[j+1]: li[j], li[j+1] = li[j+1], li[j] exchange = True if not exchange: return li = list(range(1000)) random.shuffle(li) #將函數打亂 # print(li) bubble_sort_2(li) #print(li)
import time def cal_time(func): def wrapper(*args, **kwargs): t1 = time.time() result = func(*args, **kwargs) t2 = time.time() print("%s running time: %s secs." % (func.__name__, t2-t1)) return result return wrapper
2.選擇排序:每次選出一個最小的,比冒泡少了交換,因此速度快一些
import random from timewrap import cal_time def find_min(li): #找最小值 min_val = li[0] for i in range(1, len(li)): if li[i] < min_val: min_val = li[i] return min_val def find_min_pos(li): #找最小值位置 min_pos = 0 for i in range(1, len(li)): if li[i] < li[min_pos]: min_pos = i return min_pos @cal_time def select_sort(li): #排序方法 for i in range(len(li)-1): # i表示第i趟 # 無序區的範圍 [i, len(li)) min_pos = i for j in range(i+1, len(li)): if li[j] < li[min_pos]: min_pos = j li[i], li[min_pos] = li[min_pos], li[i] # print(li) #每趟結束以後打印 li = list(range(10000)) #random.shuffle(li) # print(li) select_sort(li) #print(li)
3.插入排序:相似於摸牌插入(摸到的牌,手裏的牌) 共須要n-1趟
時間複雜度:n方級別,速度與選擇差很少
選擇速度最快只能是n方,插入和冒泡多是n
import random from timewrap import cal_time # 什麼時候插入? 1. j位置的值小於tmp 2.j=-1 li[j+1]=tmp @cal_time def insert_sort(li): for i in range(1, len(li)): j = i - 1 tmp = li[i] while j >= 0 and li[j] > tmp: # 循環繼續的條件 li[j + 1] = li[j] j -= 1 li[j + 1] = tmp # print(li) li = list(range(10000)) # random.shuffle(li) # print(li) insert_sort(li)
# 布爾值的短路問題 def func(): print('aaa') x = -3 x > 0 and func() # func()不執行 #證實快排空間複雜度 def A(a, b): return a + b def B(a, b): return A(a + b, b) def C(x): return B(x // 2, x + 5) x = C(10) print(x)
4.快速排序:重點!必須掌握!
取一個元素歸位,遞歸完成排序
算法關鍵點:整理(partition)、遞歸()
空位的概念,左右尋找(從後開始)
降序排序,只需將兩個while條件中的與tmp比較的大於小於號寫反
import sys import random from timewrap import cal_time sys.setrecursionlimit(1100) #設置遞歸深度 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) @cal_time def quick_sort(li): #封裝參數的非遞歸函數 _quick_sort(li, 0, len(li) - 1) def partition(li, left, right): i = random.randint(left, right) #隨機化選一個,最壞機率下降 li[left], li[i] = li[i], li[left] #隨機化選一個,最壞機率下降 tmp = li[left] while left < right: while left < right and li[right] >= tmp: right -= 1 li[left] = li[right] while left < right and li[left] <= tmp: left += 1 li[right] = li[left] li[left] = tmp return left # @cal_time # def sys_sort(li): #系統自帶的排序,底層是c語言,因此速度快 # li.sort() li = list(range(100000, -1, -1)) #把數據倒着 random.shuffle(li) quick_sort(li) # sys_sort(li)
5.堆排序:難!
樹與二叉樹簡介:樹是一種數據結構,能夠遞歸定義的數據結構
樹的度:最多節點孩子的個數;根節點的度:其對應孩子的個數
二叉樹:度不超過2的樹(節點最多有兩個叉),分左右孩子
滿二叉樹:每個結點都達到最大值
徹底二叉樹:葉結點只能出如今最下層和次下層,且最下面一層的結點都集中在最左邊
二叉樹的存儲方式:鏈式存儲方式--存。。、順序存儲方式(列表)--存徹底二叉樹
i表示下標:左孩子:2i+1 右孩子:2i+2 孩子找父親:(i-1)//2
堆:大根堆:一顆徹底二叉樹,任一節點都比孩子節點大 (排完升序)
小根堆:一顆徹底二叉樹,任一節點都比孩子節點小(排完降序)
堆的向下調整性質 :左右子樹都是堆,但自身不是堆,一次調整便可成堆!
挨個出數:棋子從最後一個非葉子節點開始,退休,棋子,調整
構造堆:從子樹開始從下往上調整
堆排序過程:1.創建堆2.獲得最大元素3.去掉堆頂,將堆最後一個元素放到堆頂
4.堆頂元素爲第二大元素 5.重複3,直到堆變空
代碼書寫:high表示最後元素的位置;堆排序函數,重點是兩個條件
問題:topK問題,如今有n個數,設計算法找出前k大的數(k<n)
1.排序後切片 O(nlogn +k) 2.LowB三人組思想 O(kn)
3.堆排序:創建小根堆, O(nlogk) 建堆klogk +調整 (n-k)logk = O(nlogk)
或用內置模塊
import random from timewrap import cal_time # def sift(li, low, high): #調整函數,重點是兩個條件 logn # # low 表示根位置 high 表示最後元素的位置 # tmp = li[low] # i = low # i指向空位 # j = 2 * i + 1 # j指向孩子 # # 把tmp寫回來有兩種條件 1. tmp > li[j] 2. j位置沒有值 (也就是i已是葉子了) # while j <= high: # 對應退出條件2 # if j + 1 <= high and li[j+1] > li[j]: # 若是右孩子存在且右孩子更大 # j += 1 # if li[j] > tmp: # li[i] = li[j] # i = j # j = 2 * i + 1 # else: # 對應退出條件1 # break # li[i] = tmp # # @cal_time # def heap_sort(li): # 堆排函數 # n = len(li) # # 1. 創建堆 # for low in range(n//2-1, -1, -1): #倒着走 # sift(li, low, n-1) # high=n-1 # # print(li) # # 2. 挨個出數 退休-棋子-調整 # for high in range(n-1, -1, -1): # li[0], li[high] = li[high], li[0] #退休 # sift(li, 0, high-1) #注意是high-1 # # print(li) # # # li = list(range(100000)) # random.shuffle(li) # #print(li) # heap_sort(li) import heapq li = [2, 5, 7, 8, 9, 6, 1, 4, 3] heapq.heapify(li) print(li) # 小根堆,建堆的過程 heapq.heappush(li, 0) # 往堆里加入 print(li) print(heapq.heappop(li)) # 返回最小的值 print(heapq.heappop(li)) # 返回最小的值 print(heapq.nlargest(5, li)) # 5個最大的 print(heapq.nsmallest(5, li)) # 5個最小的
6.歸併排序:條件分兩段有序,一次歸併
一個列表一次歸併;兩個列表一次歸併
遞歸歸併 時間複雜度:O(nlogn) 空間複雜度:O(n)
import random from timewrap import cal_time # def merge_two(li1, li2): #兩個有序列表 一次歸併 # li = [] # i = 0 # j = 0 # while i < len(li1) and j < len(li2): # if li1[i] <= li2[j]: # li.append(li1[i]) # i += 1 # else: # li.append(li2[j]) # j += 1 # while i < len(li1): # li.append(li1[i]) # i += 1 # while j < len(li2): # li.append(li2[j]) # j += 1 # return li #不用寫回,直接return便可 def merge(li, low, mid, high): # 一個列表 一次歸併代碼 li_tmp = [] i = low j = mid + 1 while i <= mid and j <= high: if li[i] <= li[j]: li_tmp.append(li[i]) i += 1 else: li_tmp.append(li[j]) j += 1 while i <= mid: # 左邊剩下 li_tmp.append(li[i]) i += 1 while j <= high: # 右邊剩下,兩個while二選一 li_tmp.append(li[j]) j += 1 for i in range(len(li_tmp)): # 將tmp值寫回,tmp值寫回列表 li[i + low] = li_tmp[i] def _merge_sort(li, low, high): # 遞歸歸併排序 if low < high: # 2個元素及以上 mid = (low + high) // 2 _merge_sort(li, low, mid) # 分解 _merge_sort(li, mid + 1, high) # 分解 # print(li[low:mid + 1], li[mid + 1:high + 1]) merge(li, low, mid, high) # 合併 # print(li[low:high + 1]) # li = [10, 4, 6, 3, 8, 2, 5, 7] # _merge_sort(li, 0, len(li) - 1) # print(li) @cal_time def merge_sort(li): _merge_sort(li, 0, len(li) - 1) li = list(range(100000)) random.shuffle(li) merge_sort(li)
1 三種排序算法的時間複雜度都是O(nlogn) 2 3 通常狀況下,就運行時間而言: 4 快速排序 < 歸併排序 < 堆排序 5 6 三種排序算法的缺點: 7 快速排序:極端狀況下排序效率低 8 歸併排序:須要額外的內存開銷 9 堆排序:在快的排序算法中相對較慢
小結:挨着換的穩定
快排、歸併必須理解!排序理解:思路,代碼寫法,時間複雜度下週:不經常使用的排序方式:希爾排序、計數排序、桶排序、基數排序