边/分支
父节点
子结点
兄弟结点
叶结点
深度/层级
高度
路径
后代
文件系统
域名系统
xml/html文档结构
hello,world
所以分支结点都存在左子树和右子树,并且所有的叶子都在同一层上,这样的二叉树称为满二叉树
满二叉树是同样深度的二叉树中结点最多的
# 二叉树是一种逻辑结构,物理结构同样可以用数组或是链表完成,这里是数组实现的二叉树,不推荐数组,虽然数组也有一定的特点在里面
def binary_tree(r):
return [r, [], []]
#
def insert_left(root, new_branch):
t = root.pop(1)
if len(t) > 1:
root.insert(1, [new_branch, t, []])
else:
root.insert(1,[new_branch, [], []])
return root
def insert_right(root, new_branch):
t = root.pop(2)
if len(t) > 1:
root.insert(2, [new_branch, [], [t]])
else:
root.insert(2,[new_branch, [], []])
return root
def get_root(root):
return root[0]
def set_root(root, data):
root[0] = data
def get_left_child(root):
return root[1]
def get_right_child(root):
return root[2]
r = binary_tree(3)
insert_left(r,4)
insert_left(r,5)
insert_right(r,6)
insert_right(r,7)
l = get_left_child(r)
print(l)
# 用链表实现的二叉树
class BinaryTree(object):
def __init__(self, root):
self.key = root
self.left_child = None
self.right_child = None
def insert_left(self, data):
if self.left_child == None:
self.left_child = BinaryTree(data)
else:
t = BinaryTree(data)
t.left_child = self.left_child
self.left_child = t
def insert_right(self, data):
if self.right_child == None:
self.right_child = BinaryTree(data)
else:
t = BinaryTree(data)
t.right_child = self.right_child
self.right_child = t
def get_right_child(self):
return self.right_child
def get_left_child(self):
return self.left_child
# 取出当前结点的数据
def get_root(self):
return self.key
# 设置当前结点的值
def set_root(self, value):
self.key = value
# 生成二叉树的结构
tree = BinaryTree(0)
tree.insert_left(3)
tree.insert_left(1)
tree.get_left_child().insert_right(4)
tree.insert_right(6)
tree.insert_right(2)
# 这个二叉树层级为2,根结点(第0层)为数据0,第一层为1,2,第二层为 3,4,6
不论哪种遍历,其实原理都是一样的。比如中序遍历(左中右的顺序),也就是先从最左边的最左结点开始,然后此子树的中间结点(当前树的根结点),然后此子树的右节点,此时这棵左子树已经遍历完成,把当前遍历完成的左子树看作一个整体,找到这棵树是哪个结点的左子节点,然后遍历此左子节点的中间结点,以此类推···
二叉树的遍历从结点的相对位置来看的话,分为以下四种,前序,中序,后序,层级。
二叉树的遍历从宏观的角度看,分为深度优先遍历(前序,中序,后序),广度优先遍历(层级)
以下的遍历代码是基于递归实现
# 递归实现
def pre_order(tree):
if tree:
print(tree.get_root())
pre_order(tree.get_left_child())
pre_order(tree.get_right_child())
# 利用栈的思想
def pre_order(tree_node):
s = Stack()
while tree_node or (not s.is_empty):
while tree_node != None:
print(tree_node.get_root())
s.push(tree_node)
tree_node = tree_node.get_left_child()
if not s.is_empty():
tree_node = s.pop()
tree_node = tree_node.get_right_child()
def in_order(tree):
if tree != None:
in_order(tree.get_left_child())
print(tree.get_root())
in_order(tree.get_right_child())
def post_order(tree):
if tree != None:
post_order(tree.get_left_child())
post_order(tree.get_right_child())
print(tree.get_root())
# 利用队列的数据结构
def level_order(root):
q = queue()
q.enqueue(root)
while not q.is_empty():
current_root = q.dequeue()
print(current_root.get_root())
if current_root.left_child != None:
q.enqueue(current.left)
if current_root.right_child != None:
q.enqueue(current.right)
有这样一个特点(堆次序):任何一条路径都是已经排好序的有序数列
最小堆
最大堆
# 最小堆实现(python标准库也自带heapq模块,这里为自己实现的逻辑)
class BinHeap(object):
def __init__(self):
# 这里给一个初始元素占位,是为了下面的计算方便(parent_index = 2 * left_child_index = 2 * right_child_index + 1)
self._heap = [0]
self.current_size = 0
# 有序的添加数据
def heappush(self, data):
self._heap.append(data)
self.current_size += 1
# 避免current_size的变化,用新的变量引用
child_index = self.current_size
while child_index // 2 > 0:
if self._heap[child_index] < self._heap[child_index // 2]:
self._heap[child_index], self._heap[child_index // 2] = self._heap[child_index // 2], self._heap[child_index]
child_index = child_index // 2
# 删除堆中最小元素并返回
def heappop(self):
# 将最小值抛出,并将当前最后的一个值填充给抛出的位置
rm_data = self._heap[1]
self._heap[1] = self._heap[self.current_size]
self.current_size -= 1
self._heap.pop(1)
current_index = 1
while current_index * 2 <= self.current_size:
min_index = self.min_child(current_index)
if self._heap[current_index] > self._heap[min_index]:
self._heap[current_index], self._heap[min_index] = self._heap[min_index], self._heap[current_index]
current_index = min_index
return rm_data
# 辅助函数,帮助pop_data选择子结点的最小索引并返回最小索引
def min_child(self, current_index):
# 如果只有一个子节点的情况,因为是完全二叉树,必然有左子节点的存在
if 2 * current_index + 1 > self.current_size:
return 2 * current_index
if self._heap[2 * current_index] > self._heap[2 * current_index + 1]:
return 2 * current_index + 1
return 2 * current_index
# 将传进来的列表转化成为堆结构
def heapify(self, l):
# 由于列表的长度肯定是树的最后一个值,那么也就是说这个值的父节点和他比较大小即可,一直比到根节点结束
l = [0] + l
current_index = len(l)
fath_index = current_index // 2
# 同样的下沉法
while fath_index > 0:
min_index = self.min_child(fath_index)
if self._heap[fath_index] > self._heap[min_index]:
self._heap[fath_index], self._heap[min_index] = self._heap[min_index], self._heap[fath_index]
fath_index -= 1
return l[1: ]
概念:保证左子节点的值小于其父结点,右子结点的值大于其父结点
概念:将中缀表达式转换为解析树结构表示
算法:
# 将中缀表达式转换为解析树结构表示
def build_parse_tree(tokens):
token_list = tokens.split(" ")
# 栈用来保存当前结点
s = Stack()
# 最开始构建一个空树
root = BinaryTree("")
s.push(root)
current_root = root
for i in token_list():
if i == "(":
current_root.insert_left("")
s.push(current_root)
current_root = current_root.get_left_child()
elif isinstance(i, int):
current_root.set_root(int(i))
parent = s.pop()
current_root = parent
elif i in ["+", "-", "*", "/"]:
current_root.set_root(i)
current_root.insert_right("")
s.push(current_root)
current_root = current_root.get_right_child()
elif i == ")":
current_root = s.pop()
else:
raise ValueError("wrong")
return root