標籤:學習筆記
裝飾器應用練習一、實現一個cache裝飾器,實現可到期被清除的功能
- 簡化設計,函數的形參定義不包含可變位置參數、可變關鍵詞參數和keyword-only參數
- 可以不考慮緩衝滿了之後的換出問題1)原始
def cache(fn): import inspect local_cache = {} def wrapper(*args, **kwargs): sig = inspect.signature(fn) params = sig.parameters param_names = list(params.keys()) temp_dict = {} #處理位置參數 for k, v in enumerate(args): temp_dict[param_names[k]] = v #更新關鍵字參數值 temp_dict.update(kwargs) #更新預設值 for k, v in params.items(): temp_dict[k] = v.default #排序產生元組 temp_tuple = tuple(sorted(temp_dict.items())) if temp_tuple not in local_cache.keys(): local_cache[temp_tuple] = fn(*args, **kwargs) return local_cache[temp_tuple] return wrapperimport time@cachedef add(x, y, z): time.sleep(2) return x + y + z
2)加入到期判斷
import inspectfrom datetime import datetimedef cache(duration): def _cache(fn): local_cache={} def wrapper(*args, **kwargs): def expire_cache(cache:dict): expire_list = [] for k,(_,stamp) in cache.items(): delta = (datetime.now().timestamp() - stamp) if delta > duration: expire_list.append(k) for k in expire_list: cache.pop(k) expire_cache(local_cache) sig=inspect.signature(fn) params=sig.parameters param_names=list(params.keys()) param_dict={} for k,v in enumerate(args): param_dict[param_names[k]] = v param_dict.update(kwargs) for k, v in params.items(): if k not in param_dict.keys(): param_dict[k] = v.default param_keys=tuple(sorted(param_dict.items())) if param_keys not in local_cache.keys(): local_cache[param_keys]=(fn(*args,**kwargs), datetime.now().timestamp()) return local_cache[param_keys][0] return wrapper return _cache
二、寫一個命令分發器
- 程式員可以方便的註冊函數到某一個命令,使用者輸入命令時,路由到註冊的函數
- 如果此命令沒有對應的註冊函數,執行預設函數
- 使用者輸入用input(">>")
def cmd_dispatcher(): #封裝 cmd_dict = {} def reg(cmd): def _reg(fn): cmd_dict[cmd] = fn return fn return _reg @reg(‘default_func‘) def default_func(): print(‘default‘) return def dispatcher(): while True: cmd = input(‘>>‘) if cmd == ‘quit‘: return cmd_dict.get(cmd, default_func)() return reg, dispatcher #封裝reg, dispatcher = cmd_dispatcher() #封裝&解構@reg(‘add‘)def add(): #add=reg(‘add‘)(add) print(1) returndispatcher()
二叉樹遍曆廣度優先遍曆
- 層序遍曆,按照樹的層次,從第一層開始,自左向右遍曆元素深度優先遍曆
- 設樹的根結點為D,左子樹為L,右子樹為R,且要求L一定在R之前,則有下面幾種遍曆方式:
- 前序走訪,也叫先序遍曆、也叫先根遍曆,DLR
- 中序遍曆,也叫中根遍曆,LDR
- 後序遍曆,也叫後根遍曆,LRD
- 遍曆序列:將樹中所有元素遍曆一遍後,得到的元素的序列。將階層轉換成了線性結構
前序走訪DLR
- 從根結點開始,先左子樹後右子樹
- 每個子樹內部依然是先根結點,再左子樹後右子樹。遞迴遍曆
- 遍曆序列
中序遍曆LDR
- 從根結點的左子樹開始遍曆,然後是根結點,再右子樹
- 每個子樹內部,也是先左子樹,後根結點,再右子樹。遞迴遍曆
- 遍曆序列
- 左圖
- GDHB A IECF
- 右圖
- GDHB A EICF
後序遍曆LRD
- 先左子樹,後右子樹,再根結點
- 每個子樹內部依然是先左子樹,後右子樹,再根結點。遞迴遍曆
- 遍曆序列
堆排序堆Heap
- 堆是一個完全二叉樹
- 每個非葉子結點都要大於或者等於其左右孩子結點的值稱為大頂堆
- 每個非葉子結點都要小於或者等於其左右孩子結點的值稱為小頂堆
- 根結點一定是大頂堆中的最大值,一定是小頂堆中的最小值
大頂堆
- 完全二叉樹的每個非葉子結點都要大於或者等於其左右孩子結點的值稱為大頂堆
- 根結點一定是大頂堆中的最大值
小頂堆
- 完全二叉樹的每個非葉子結點都要小於或者等於其左右孩子結點的值稱為小頂堆
- 根結點一定是小頂堆中的最小值
理論實現1、構建完全二叉樹
- 待排序數字為 30,20,80,40,50,10,60,70,90
- 構建一個完全二叉樹存放資料,並根據性質5對元素編號,放入順序的資料結構中
- 構造一個列表為[0,30,20,80,40,50,10,60,70,90]
2、構建大頂堆——核心演算法
- 度數為2的結點A,如果它的左右孩子結點的最大值比它大的,將這個最大值和該結點交換
- 度數為1的結點A,如果它的左孩子的值大於它,則交換
- 如果結點A被交換到新的位置,還需要和其孩子結點重複上面的過程
2.1 構建大頂堆——起點結點的選擇
- 從完全二叉樹的最後一個結點的雙親結點開始,即最後一層的最右邊葉子結點的父結點開始結點數為n,則起始結點的編號為n//2(性質5)
2.2 構建大頂堆——下一個結點的選擇
- 從起始結點開始向左找其同層結點,到頭後再從上一層的最右邊結點開始繼續向左逐個尋找,直至根結點
2.3 大頂堆的目標
3、排序
- 將大頂堆根結點這個最大值和最後一個葉子結點交換,那麼最後一個葉子結點就是最大值,將這個葉子結點排除在待排序結點之外
- 從根結點開始(新的根結點),重新調整為大頂堆後,重複上一步
代碼實現1.列印樹結構(非必要,方便查看每步操作對樹結構的改變)1)方法一 置中對齊
def show_tree(lst, unit_width=2): from math import log2, ceil length = len(lst) depth = ceil(log2(length + 1)) width = 2 ** depth - 1 index= 0 for i in range(depth): for j in range(2 ** i): print(‘{:^{}}‘.format(lst[index], width * unit_width), end = ‘ ‘ * unit_width) index += 1 if index >= length: break width //= 2 print()
2)方法二 投影柵格實現
from math import ceil, log2#投影柵格實現def print_tree(array): ‘‘‘ ‘‘‘ index = 1 depth = ceil(log2(len(array))) sep = ‘ ‘ for i in range(depth): offset = 2 ** i print(sep * (2 ** (depth - i -1) - 1), end = ‘‘) line = array[index : index + offset] for j, x in enumerate(line): print("{:>{}}".format(x, len(sep)), end = ‘‘) interval = 0 if i == 0 else 2 ** (depth - i) - 1 if j < len(line) - 1: print(sep * interval, end = ‘‘) index += offset print()
2.堆排序實現
def heap_sort(lst:list): ‘‘‘ 堆排序 :type lst: list :rtype: list ‘‘‘ length = len(lst) lst.insert(0,0) # 前插0為了索引和結點號能夠對應上,索引不必加一,便於理解,輸出時切片即可 def heap_adjust(start, end): ‘‘‘ 調整當前節點 調整結點的起點在n//2,保證所有調整結點都有孩子結點 :param end: 待比較個數 :param start: 當前節點下標 :rtype: None ‘‘‘ while 2 * start <= end: # 如果該結點下還有孩子則進入迴圈,否則跳出 lchild_index = 2 * start #該結點號*2 為左孩子結點 max_child_index = lchild_index # if lchild_index < end and lst[lchild_index + 1] > lst[lchild_index]: # 如果有右孩子並且右孩子比左孩子的數大,則更新最大值索引 max_child_index = lchild_index + 1 if lst[max_child_index] > lst[start]: #孩子結點比較完後與父結點比較,最大值放到父結點,並且下一次迭代的父結點是本次最大孩子索引 lst[start], lst[max_child_index] = lst[max_child_index], lst[start] start = max_child_index else: # 如果父結點最大則直接跳出,因為排頂堆從編號最大的子樹開始調整,即保證了本次最大孩子結點與其孩子結點已經形成了頂堆 break for st in range(length//2, 0, -1): # 調整為大頂堆 heap_adjust(st, length) for end in range(length, 1, -1): #sort排序 根結點與最後結點交換,每次迭代刨除最後結點,直至只剩根結點 lst[1], lst[end] = lst[end], lst[1] heap_adjust(1, end - 1) return lst[1:]
- 時間複雜度O(nlogn),平均時間複雜度O(nlogn)
- 空間複雜度O(1)
- 不穩定排序
Python第五周 學習筆記(2)