一、二叉树的遍历
树结构分为广度优先遍历和深度优先遍历。广度遍历是一层一层的遍历树中的元素,这种遍历方式需要借助队列的方式,左右子树分别入队列,利用队列先进先出的特性,按层对树进行遍历。深度遍历有又分为前序遍历、中序遍历、后序遍历,针对这三种遍历方式,又有利用递归的形式进行遍历,利用堆栈结构进行遍历。
尽管树的遍历看起来有很多种方式,但是我们平常更多用到的是基于递归方式的深度遍历,故而读者看到这些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----------------------------')