二叉樹
概念
二叉樹是n(n>=0)個結點的有限集合,該集合或者為空集(稱為空二叉樹),
或者由一個根結點和兩棵互不相交的、分別稱為根結點的左子樹和右子樹組成。
特點
每個結點最多有兩顆子樹,所以二叉樹中不存在度大於2的結點
左子樹和右子樹是有順序的,次序不能任意顛倒
即使樹中某結點只有一棵子樹,也要區分它是左子樹還是右子樹
性質
1)在二叉樹的第 i 層上最多有 2 i-1 個節點 。(i>=1)(1) 若 i=1,則該結點是二叉樹的根,無雙親, 否則,編號為 [i/2] 的結點為其雙親結點; (2) 若 2i>n,則該結點無左孩子, 否則,編號為 2i 的結點為其左孩子結點; (3) 若 2i+1>n,則該結點無右孩子結點, 否則,編號為2i+1 的結點為其右孩子結點。
層
度
類型
斜樹
所有的結點都只有左子樹的二叉樹叫左斜樹。
所有結點都是只有右子樹的二叉樹叫右斜樹。
這兩者統稱為斜樹。

滿二叉樹
在一棵二叉樹中。
如果所有分支結點都存在左子樹和右子樹,
並且所有葉子都在同一層上,這樣的二叉樹稱為滿二叉樹。
滿二叉樹的特點有:
1)葉子只能出現在最下一層。出現在其它層就不可能達成平衡。
2)非葉子結點的度一定是2。
3)在同樣深度的二叉樹中,滿二叉樹的結點個數最多,葉子數最多。

完全二叉樹
對一顆具有n個結點的二叉樹按層編號
如果編號為 i(1<=i<=n) 的結點與同樣深度的滿二叉樹中編號為 i 的結點在二叉樹中位置完全相同
則這棵二叉樹稱為完全二叉樹。
特點
4)如果結點度為1,則該結點只有左孩子,即沒有右子樹。
5)同樣結點數目的二叉樹,完全二叉樹深度最小。
注:滿二叉樹一定是完全二叉樹,但反過來不一定成立。

存儲結構
順序存儲
二叉樹的順序存儲結構就是使用一維數組存儲二叉樹中的結點,
並且結點的存儲位置,就是數組的下標索引。

二叉鏈表
既然順序存儲不能滿足二叉樹的存儲需求,那么考慮采用鏈式存儲。
由二叉樹定義可知,二叉樹的每個結點最多有兩個孩子。
因此,可以將結點數據結構定義為一個數據和兩個指針域。

二叉樹實現
定義一個左子樹, 定義一個柚子樹, 以及值
class BinTree: def __init__(self,value=None,left=None,right=None): self.value=value self.left=left #左子樹 self.right=right #右子樹
二叉樹遍歷
先序遍歷
先處理根, 之后是左子樹, 然后是右子樹
中序遍歷
先處理左子樹, 之后是根, 然后是右子樹
后序遍歷
先處理左子樹, 之后是右子樹, 最后是根
代碼示例
def preTraverse(root): ''' 先序遍歷 ''' if root is not None: print(root.value) preTraverse(root.left) preTraverse(root.right) def midTraverse(root): ''' 中序遍歷 ''' if root is not None: midTraverse(root.left) print(root.value) midTraverse(root.right) def afterTraverse(root): ''' 后序遍歷 ''' if root is not None: afterTraverse(root.left) afterTraverse(root.right) print(root.value)
堆
最大堆 / 最小堆
最大 / 小堆是一棵完全二叉樹,非葉子結點的值不大 / 小於左孩子和右孩子的值
構建原理
初始數組為:9,3,7,6,5,1,10,2
按照完全二叉樹,將數字依次填入。
填入后,找到最后一個結點(本示例為數字2的節點),從它的父節點(本示例為數字6的節點)
開始調整。根據性質,小的數字往上移動;至此,第1次調整完成。
注意,被調整的節點,還有子節點的情況,需要遞歸進行調整。
第二次調整,是數字6的節點數組下標小1的節點(比數字6的下標小1的節點是數字7的節點),
用剛才的規則進行調整。以此類推,直到調整到根節點。
元素插入
以上個最小堆為例,插入數字0。
數字0的節點首先加入到該二叉樹最后的一個節點,依據最小堆的定義,自底向上,遞歸調整。
元素刪除
代碼實現
最小堆
class MinHeap(object): """最小堆""" def __init__(self): self.data = [] # 創建堆 self.count = len(self.data) # 元素數量 # def __init__(self, arr): # self.data = copy.copy(arr) # self.count = len(self.data) # i = self.count / 2 # while i >= 1: # self.shiftDown(i) # i -= 1 def size(self): return self.count def isEmpty(self): return self.count == 0 def insert(self, item): # 插入元素入堆 self.data.append(item) self.count += 1 self.shiftup(self.count) def shiftup(self, count): # 將插入的元素放到合適位置,保持最小堆 while count > 1 and self.data[(count / 2) - 1] > self.data[count - 1]: self.data[(count / 2) - 1], self.data[count - 1] = self.data[count - 1], self.data[(count / 2) - 1] count /= 2 def extractMin(self): # 出堆 if self.count > 0: ret = self.data[0] self.data[0], self.data[self.count - 1] = self.data[self.count - 1], self.data[0] self.data.pop() self.count -= 1 self.shiftDown(1) return ret def shiftDown(self, count): # 將堆的索引位置元素向下移動到合適位置,保持最小堆 while 2 * count <= self.count: # 證明有孩子 j = 2 * count if j + 1 <= self.count: # 證明有右孩子 if self.data[j] < self.data[j - 1]: j += 1 if self.data[count - 1] <= self.data[j - 1]: # 堆的索引位置已經小於兩個孩子節點,不需要交換了 break self.data[count - 1], self.data[j - 1] = self.data[j - 1], self.data[count - 1] count = j
最大堆
""" 最大堆 """ class MaxHeap(object): # def __init__(self): # self.data = [] # 創建堆 # self.count = len(self.data) # 元素數量 def __init__(self, arr): self.data = copy.copy(arr) self.count = len(self.data) i = self.count / 2 while i >= 1: self.shiftDown(i) i -= 1 def size(self): return self.count def isEmpty(self): return self.count == 0 def insert(self, item): # 插入元素入堆 self.data.append(item) self.count += 1 self.shiftup(self.count) def shiftup(self, count): # 將插入的元素放到合適位置,保持最大堆 while count > 1 and self.data[(count / 2) - 1] < self.data[count - 1]: self.data[(count / 2) - 1], self.data[count - 1] = self.data[count - 1], self.data[(count / 2) - 1] count /= 2 def extractMax(self): # 出堆 if self.count > 0: ret = self.data[0] self.data[0], self.data[self.count - 1] = self.data[self.count - 1], self.data[0] self.data.pop() self.count -= 1 self.shiftDown(1) return ret def shiftDown(self, count): # 將堆的索引位置元素向下移動到合適位置,保持最大堆 while 2 * count <= self.count: # 證明有孩子 j = 2 * count if j + 1 <= self.count: # 證明有右孩子 if self.data[j] > self.data[j - 1]: j += 1 if self.data[count - 1] >= self.data[j - 1]: # 堆的索引位置已經大於兩個孩子節點,不需要交換了 break self.data[count - 1], self.data[j - 1] = self.data[j - 1], self.data[count - 1] count = j
heapq 模塊
官方文檔 這里
Python 自帶的用於堆結構方便的模塊
創建堆 - 最小堆
單個添加創建堆 - heappush
import heapq data = [1,5,3,2,8,5] heap = [] for n in data: heapq.heappush(heap, n)
對已存在的序列轉化為堆 - heapify
import heapq data = [1,5,3,2,8,5] heapq.heapify(data)
對多個序列轉化為堆 - merge
import heapq num1 = [32, 3, 5, 34, 54, 23, 132] num2 = [23, 2, 12, 656, 324, 23, 54] num1 = sorted(num1) num2 = sorted(num2) res = heapq.merge(num1, num2) print(list(res)) # [2, 3, 5, 12, 23, 23, 23, 32, 34, 54, 54, 132, 324, 656]
創建堆 - 最大堆
用法同最小堆, 創建的時候將所有的值取相反數
然后在取出堆頂, 在進行取反, 即可獲得原值
import heapq data = [1, 5, 3, 2, 8, 5] li = [] for i in data: heapq.heappush(li, -i) print(li) # [-8, -5, -5, -1, -2, -3] print(-li[0]) # 8
訪問堆內容
查看最小值 - [0]
import heapq data = [1, 5, 3, 2, 8, 5] heapq.heapify(data) print(data[0]) # 1
彈出最小值 - heappop
會改變原數據, 類似於列表的 pop
import heapq data = [1, 5, 3, 2, 8, 5] heapq.heapify(data) print(heapq.heappop(data)) # 1 print(data) # [2, 5, 3, 5, 8]
向堆內推送值 - heappush
import heapq data = [1, 5, 3, 2, 8, 5] heapq.heapify(data) heapq.heappush(data, 10) print(data) # [1, 2, 3, 5, 8, 5, 10]
彈出最小值並加入一個值 - heappushpop
彈出最小值, 添加新值, 且自動排序保持是最小堆
import heapq data = [1, 5, 3, 2, 8, 5] heapq.heapify(data) print(heapq.heappushpop(data, 1)) # 1 print(data) # [1, 2, 3, 5, 8, 5]
彈出最小值並加入一個值 - heapreplace
彈出最小值, 添加新值, 且自動排序保持是最小堆
是 heappushpop 的高效版本, 在py3 中適用
import heapq data = [1, 5, 3, 2, 8, 5] heapq.heapify(data) print(heapq.heapreplace(data, 10)) # 1 print(data) # [2, 5, 3, 10, 8, 5]
k 值問題 - nlargest / nsmallest
找出堆中最小 / 大的 k 個值
import heapq data = [1, 5, 3, 2, 8, 5] heapq.heapify(data) print(data) # [1, 2, 3, 5, 8, 5] print(heapq.nlargest(2, data)) # [8, 5] print(heapq.nsmallest(2, data)) # [1, 2]
可以接收一個參數 key , 用於指定選項進行選取
用法類似於 sorted 的 key
import heapq from pprint import pprint portfolio = [ {'name': 'IBM', 'shares': 100, 'price': 91.1}, {'name': 'AAPL', 'shares': 50, 'price': 543.22}, {'name': 'FB', 'shares': 200, 'price': 21.09}, {'name': 'HPQ', 'shares': 35, 'price': 31.75}, {'name': 'YHOO', 'shares': 45, 'price': 16.35}, {'name': 'ACME', 'shares': 75, 'price': 115.65} ] cheap = heapq.nsmallest(3, portfolio, key=lambda s: s['price']) expensive = heapq.nlargest(3, portfolio, key=lambda s: s['price']) pprint(cheap) pprint(expensive) """ 輸出: [{'name': 'YHOO', 'price': 16.35, 'shares': 45}, {'name': 'FB', 'price': 21.09, 'shares': 200}, {'name': 'HPQ', 'price': 31.75, 'shares': 35}] [{'name': 'AAPL', 'price': 543.22, 'shares': 50}, {'name': 'ACME', 'price': 115.65, 'shares': 75}, {'name': 'IBM', 'price': 91.1, 'shares': 100}] """