版权声明:转载必须经过本人同意,并且注明文章来源! https://blog.csdn.net/weixin_41665360/article/details/88861801
文章目录
1、树相关概念
- 节点的度:一个节点含有的子树个数
- 树的度:树中最大的节点的度称为树的度
- 叶节点或终端节点:度为零的节点
- 父节点:一个节点含有子节点,这个节点称为子节点的父节点
- 子节点:一个节点含有的子树的根节点称为该节点的子节点
- 兄弟节点:含有相同父节点的节点互称兄弟节点
- 节点的层次:从根节点起,根节点为第1层,根节点的子节点为第2层,依此类推
- 树的高度或深度:树中节点的最大层次
- 堂兄弟节点:父节点在同一层的节点互称堂兄弟节点
- 节点的祖先:从根节点到该节点所经分支上的所有节点
- 子孙:以某节点为根的子树中任一节点都称为该节点的子孙
- 森林:由 棵互不相交的树的集合称为森林
2、树的种类
- 无序树:树中任意节点的子节点间没有顺序关系,也称自由树
- 有序树:树中任意节点的子节点间有顺序关系
- 二叉树:每个节点最多含有两个子树的树
- 完全二叉树:除最后一层外,每层节点数目均达到最大值,且最后一层节点从左到右紧密排列
- 满二叉树:所有叶节点都在最底层的完全二叉树
- 平衡二叉树(AVL树):任何节点的两棵子树高度差不大于1的二叉树
- 排序二叉树(二叉查找树)
- 若左子树不空,左子树所有节点的值均小于等于根节点值
- 若右子树不空,右子树所有节点的值均大于等于根节点值
- 左右子树也分别为二叉排序树
- 完全二叉树:除最后一层外,每层节点数目均达到最大值,且最后一层节点从左到右紧密排列
- 霍夫曼树:带权路径最短的二叉树,也称最优二叉树
- B树:对读写操作进行优化的自平衡的二叉查找树,保持数据有序,拥有多于两个子树
- 二叉树:每个节点最多含有两个子树的树
3、树的存储
- 顺序存储
- 链式存储
4、应用
- 等
- 路由协议
- 数据库索引
- 文件系统的目录结构
- 机器学习中的决策树
5、二叉树的性质
- 二叉树第 层至多有 个结点
- 深度为 的二叉树最多有 个结点
- 任一棵二叉树,叶结点数目为 ,度为 的结点总数为 ,则有:
- 具有 个结点的完全二叉树的深度为
- 对完全二叉树,从上到下,从左到右编号,编号为 的结点,左子结点编号为 右子结点编号为 ,父结点编号为 。
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、二叉树深度优先遍历
深度优先遍历包括三种情况:
- 先序遍历
- 中序遍历
- 后序遍历
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() 是因为子树全部访问完,
要进入回溯过程,取出路径中保存的最后一个标志进行判断
若是从左子树进行的回溯,则访问右子树
若是从右子树进行的回溯,则访问父结点
"""