一、二叉樹的遍歷
樹結構分爲廣度優先遍歷和深度優先遍歷。廣度遍歷是一層一層的遍歷樹中的元素,這種遍歷方式需要藉助隊列的方式,左右子樹分別入隊列,利用隊列先進先出的特性,按層對樹進行遍歷。深度遍歷有又分爲前序遍歷、中序遍歷、後序遍歷,針對這三種遍歷方式,又有利用遞歸的形式進行遍歷,利用堆棧結構進行遍歷。
儘管樹的遍歷看起來有很多種方式,但是我們平常更多用到的是基於遞歸方式的深度遍歷,故而讀者看到這些introduction不用緊張,小編已經對代碼做了註釋便於大家理解。
class Node(object):
"""節點類"""
def __init__(self, elem=-1, lchild=None, rchild=None):
self.elem = elem
self.lchild = lchild
self.rchild = rchild
class Tree(object):
"""
普通二叉樹
"""
def __init__(self):
self.root = Node() # 根節點
self.myQueue = []
def add(self, elem):
"""爲樹添加節點"""
node = Node(elem)
if self.root.elem == -1: # 如果樹是空的,則對根節點賦值
self.root = node
self.myQueue.append(self.root)
else:
treeNode = self.myQueue[0] # 此結點的子樹還沒有齊。
# print("~~", treeNode.elem, len(self.myQueue))
# 因爲是二叉樹,故而添加左子樹的時候不會移除隊首元素(因爲此時其右子樹還可以再添加),添加右子樹的時候會移除隊首元素(此時左右子樹都有,當前節點就可以移除了不需要再插入新的樹了)。
if treeNode.lchild == None:
treeNode.lchild = node
self.myQueue.append(treeNode.lchild)
else:
treeNode.rchild = node
self.myQueue.append(treeNode.rchild)
self.myQueue.pop(0) # 如果該結點存在右子樹,將此結點丟棄。
def front_digui(self, root):
"""利用遞歸實現樹的先序遍歷,深度遍歷"""
if root == None:
return
print(root.elem)
self.front_digui(root.lchild)
self.front_digui(root.rchild)
def middle_digui(self, root):
"""利用遞歸實現樹的中序遍歷,深度遍歷"""
if root == None:
return
self.middle_digui(root.lchild)
print(root.elem)
self.middle_digui(root.rchild)
def later_digui(self, root):
"""利用遞歸實現樹的後序遍歷,深度遍歷"""
if root == None:
return
self.later_digui(root.lchild)
self.later_digui(root.rchild)
print(root.elem)
def front_stack(self, root):
"""利用堆棧實現樹的先序遍歷,深度遍歷"""
if root == None:
return
myStack = []
node = root
while node or myStack:
while node: # 從根節點開始,一直找它的左子樹
print(node.elem)
myStack.append(node)
node = node.lchild
node = myStack.pop() # while結束表示當前節點node爲空,即前一個節點沒有左子樹了
node = node.rchild # 開始查看它的右子樹
def middle_stack(self, root):
"""利用堆棧實現樹的中序遍歷,深度遍歷"""
if root == None:
return
myStack = []
node = root
while node or myStack:
while node: # 從根節點開始,一直找它的左子樹
myStack.append(node)
node = node.lchild
node = myStack.pop() # while結束表示當前節點node爲空,即前一個節點沒有左子樹了
print(node.elem)
node = node.rchild # 開始查看它的右子樹
def later_stack(self, root):
"""利用堆棧實現樹的後序遍歷,深度遍歷"""
if root == None:
return
myStack1 = []
myStack2 = []
node = root
myStack1.append(node)
while myStack1: # 這個while循環的功能是找出後序遍歷的逆序,存在myStack2裏面
node = myStack1.pop()
if node.lchild:
myStack1.append(node.lchild)
if node.rchild:
myStack1.append(node.rchild)
myStack2.append(node)
while myStack2: # 將myStack2中的元素出棧,即爲後序遍歷次序
print(myStack2.pop().elem)
def level_queue(self, root):
"""利用隊列實現樹的層次遍歷,廣度遍歷"""
if root == None:
return
myQueue = []
node = root
myQueue.append(node)
while myQueue:
node = myQueue.pop(0)
print(node.elem)
if node.lchild != None:
myQueue.append(node.lchild)
if node.rchild != None:
myQueue.append(node.rchild)
if __name__ == '__main__':
"""主函數"""
elems = range(10) # 生成十個數據作爲樹節點
tree = Tree() # 新建一個樹對象
for elem in elems:
tree.add(elem) # 逐個添加樹的節點
print('隊列實現層次遍歷:', tree.level_queue(tree.root))
print()
print('遞歸實現先序遍歷:', tree.front_digui(tree.root))
print()
print('遞歸實現中序遍歷:', tree.middle_digui(tree.root))
print()
print('遞歸實現後序遍歷:', tree.later_digui(tree.root))
print()
print('堆棧實現先序遍歷:', tree.front_stack(tree.root))
print()
print('堆棧實現中序遍歷:', tree.middle_stack(tree.root))
print()
print('堆棧實現後序遍歷:', tree.later_stack(tree.root))
二、字典樹的操作
字典樹又稱前綴樹,字典樹常用於搜索提示,如當輸入一個網址,可以自動搜索出可能的選擇。小編在工作中用到最多的樹就是字典樹了,因爲小編會將字典樹固化到磁盤上,每天更新一次固化的文件,第二天會用這個字典樹支撐一些搜索業務,當然這是前期數據量不大的情況,如果數據量很大,則可能要根據時間序列構建多棵字典樹,併發的對這些樹結構進行檢索。
字典樹並不是二叉樹,一個父節點可能會有多個子節點,故而在定義樹的node類時,記錄子節點的數據結構是dict。
python的代碼如下,僅供讀者參考,另外小編在固化字典樹的時候用到的是python的pickle包,大家可自行查閱相關信息。
import _pickle as pickle
class TrieNode:
def __init__(self):
'''
定義節點的數據結構,並初始化,設置標誌位判斷是否單詞是否是完整的存在於字典樹中
'''
self.data = {}
self.flag = False
self.value = {}
class TrieTree(object):
def __init__(self):
'''
定義字典樹的數據結構並初始化根節點
'''
self.root = TrieNode()
def insert_func(self, data):
'''
字典樹的插入函數,data爲待插入的數據,類型爲字符串
'''
top_node = self.root
for i in range(len(data["word"])):
one_char = data["word"][i]
child_node = top_node.data.get(one_char) # 當前節點的子節點,因爲一個字母可能是很多字母的前綴,故而用dict存儲
if not child_node:
top_node.data[one_char] = TrieNode()
top_node = top_node.data[one_char]
top_node.flag = True
top_node.value["age"] = data["age"]
def search_func(self, data):
'''
字典樹的查找函數,data類型爲字符串
'''
top_node = self.root
for i in range(len(data)):
one_char = data[i]
top_node = top_node.data.get(one_char)
if not top_node:
return False
return top_node.flag, top_node.value
if __name__ == '__main__':
str_list = ['html', 'head', 'body', 'meta', 'div', 'link', 'a', 'comment', 'iframe', 'ul', 'li']
str_list_new = []
for i in range(len(str_list)):
str_list_new.append({
"word": str_list[i],
"age": i+1
})
TestTrieTree = TrieTree()
for one_str in str_list_new:
TestTrieTree.insert_func(one_str)
print('***********************************結果如下,True表示存在,False表示不存在******************************************')
search_list = ['html', 'table', 'body', 'tr', 'h1', 'script', 'javascript', 'iframe']
for one_str in search_list:
print(one_str, TestTrieTree.search_func(one_str))
print()
print('----------------------------------持久化存儲TestTrieTree模型---------------------------------')
trie_file = open('trie.pkl', 'wb')
pickle.dump(TestTrieTree, trie_file)
trie_file = open('trie.pkl', "rb")
trie_model = pickle.load(trie_file)
print()
# 二次插入
trie_model.insert_func({"word":"yi", "age":100})
trie_model.insert_func({"word":"shui", "age":200})
trie_model.insert_func({"word":"han", "age":300})
trie_model.insert_func({"word":"cheng", "age":400})
print('----------------------------------insert over----------------------------------')
print(trie_model.search_func('yi'))
print(trie_model.search_func('shui'))
print(trie_model.search_func('han'))
print(trie_model.search_func('cheng'))
print('-------------------search over----------------------------')