数据结构与算法(python):二叉树

版权声明:转载必须经过本人同意,并且注明文章来源! https://blog.csdn.net/weixin_41665360/article/details/88861801

1、树相关概念

  • 节点的度:一个节点含有的子树个数
  • 树的度:树中最大的节点的度称为树的度
  • 叶节点或终端节点:度为零的节点
  • 父节点:一个节点含有子节点,这个节点称为子节点的父节点
  • 子节点:一个节点含有的子树的根节点称为该节点的子节点
  • 兄弟节点:含有相同父节点的节点互称兄弟节点
  • 节点的层次:从根节点起,根节点为第1层,根节点的子节点为第2层,依此类推
  • 树的高度或深度:树中节点的最大层次
  • 堂兄弟节点:父节点在同一层的节点互称堂兄弟节点
  • 节点的祖先:从根节点到该节点所经分支上的所有节点
  • 子孙:以某节点为根的子树中任一节点都称为该节点的子孙
  • 森林:由 m m 棵互不相交的树的集合称为森林

2、树的种类

  • 无序树:树中任意节点的子节点间没有顺序关系,也称自由树
  • 有序树:树中任意节点的子节点间有顺序关系
    • 二叉树:每个节点最多含有两个子树的树
      • 完全二叉树:除最后一层外,每层节点数目均达到最大值,且最后一层节点从左到右紧密排列
        • 满二叉树:所有叶节点都在最底层的完全二叉树
      • 平衡二叉树(AVL树):任何节点的两棵子树高度差不大于1的二叉树
      • 排序二叉树(二叉查找树)
        • 若左子树不空,左子树所有节点的值均小于等于根节点值
        • 若右子树不空,右子树所有节点的值均大于等于根节点值
        • 左右子树也分别为二叉排序树
    • 霍夫曼树:带权路径最短的二叉树,也称最优二叉树
    • B树:对读写操作进行优化的自平衡的二叉查找树,保持数据有序,拥有多于两个子树

3、树的存储

  1. 顺序存储
  2. 链式存储

4、应用

  1. x m l   h t m l xml~html
  2. 路由协议
  3. m y s q l mysql 数据库索引
  4. 文件系统的目录结构
  5. 机器学习中的决策树

5、二叉树的性质

  1. 二叉树第 i i 层至多有 2 i 1 2^{i-1} 个结点
  2. 深度为 k k 的二叉树最多有 2 k 1 2^k-1 个结点
  3. 任一棵二叉树,叶结点数目为 N 0 N_0 ,度为 2 2 的结点总数为 N 2 N_2 ,则有: N 0 = N 2 + 1 N_0=N_2+1
  4. 具有 n n 个结点的完全二叉树的深度为 l o g 2 ( n + 1 ) \lceil log_2(n+1)\rceil
  5. 对完全二叉树,从上到下,从左到右编号,编号为 i i 的结点,左子结点编号为 2 i 2i 右子结点编号为 2 i + 1 2i+1 ,父结点编号为 i / 2 \lfloor i/2\rfloor

6、Python 实现

6.1、定义树结点及二叉树

# 定义二叉树结点
class Node(object):
	"""定义结点"""
	def __init__(self, item):
		self.elem = item
		self.lchild = None
		self.rchild = None

# 定义二叉树
class Tree(object):
	"""二叉树"""
	def __init__(self):
		self.root = None

6.2、向二叉树添加元素

	# 添加树结点
	def add(self, item):
		node  = Node(item)
		if self.root is None:
			self.root = node
			return
		queue = []
		queue.append(self.root)
		while queue:
			cur_node = queue.pop(0)
			if cur_node.lchild is None:
				cur_node.lchild = node
				return
			else:
				queue.append(cur_node.lchild)
			if cur_node.rchild is None:
				cur_node.rchild = node
				return
			else:
				queue.append(cur_node.rchild)

6.3、二叉树广度优先遍历

	def breadth_travel(self):
	"""广度优先遍历:借助队列实现"""
		if self.root is None:
			return
		queue = [self.root]
		while queue:
			cur_node = queue.pop(0)
			print(cur_node.elem)
			if cur_node.lchild is not None:
				queue.append(cur_node.lchild)
			if cur_node.rchild is not None:
				queue.append(cur_node.rchild)

6.4、二叉树深度优先遍历

深度优先遍历包括三种情况:

  1. 先序遍历
  2. 中序遍历
  3. 后序遍历

6.4.1、先序遍历

# 递归实现
def preorder(self, root):
	if root == None:
		return
	print(root.elem)
	self.preorder(root.lchild)
	self.preorder(root.rchild)
# 非递归实现
def preorder(self, root):
	"""通过栈实现"""
	stack = []   # 用于保存访问路径,使算法能够回溯
	cur_node = root
	while cur_node or stack:
		if cur_node:
			print(cur_node.elem)
			stack.append(cur_node) # 保存访问过的结点
			cur_node = cur_node.lchild
		else:                      # 开始回溯
			cur_node = stack.pop()
			cur_node = cur_node.rchild

6.4.2、中序遍历

# 递归实现
def inorder(self, root):
	if root == None:
		return
	self.inorder(root.lchild)
	print(root.elem)
	self.inorder(root.rchild)
# 非递归实现
def inorder(self, root):
	"""通过栈实现"""
	stack = []   # 用于保存访问路径,使算法能够回溯
	cur_node = root
	while cur_node or stack:
		if cur_node:
			stack.append(cur_node) # 保存路径
			cur_node = cur_node.lchild
		else:                      # 开始回溯
			cur_node = stack.pop()
			print(cur_node.elem)
			cur_node = cur_node.rchild

6.4.3、后序遍历

# 递归实现
def postorder(self, root):
	if root == None:
		return
	self.postorder(root.lchild)
	self.postorder(root.rchild)
	print(root.elem)
def postorder(self, root):
	"""通过栈实现"""
	stack = []   # 用于保存访问路径,使算法能够回溯
	flag = []    # 用于记录路径经过结点的次数,控制访问结点的时机
	cur_node = root
	while cur_node or stack:
		if cur_node:
			stack.append(cur_node) # 保存路径
			flag.append(1)         # 记录第一次经过该结点,开始访问左子树
			cur_node = cur_node.lchild
		else:                      # 开始回溯
			cur_node = stack.pop()
			f = flag.pop()
			if f == 1:             # 经过该结点一次,左子树已访问完
				stack.append(cur_node)
				flag.append(2)     # 记录第二次经过该结点,开始访问右子树
				cur_node = cur_node.rchild
			else:                  # f == 2 两棵子树访问完,开始访问父结点
				print(cur_node.elem)
				cur_node = None
				"""
				这里不是 cur_node == stack.pop() 是因为子树全部访问完,
				要进入回溯过程,取出路径中保存的最后一个标志进行判断
				若是从左子树进行的回溯,则访问右子树
				若是从右子树进行的回溯,则访问父结点
				"""

猜你喜欢

转载自blog.csdn.net/weixin_41665360/article/details/88861801