东阳的学习记录,坚持就是胜利!
树的基本概念
树的定义
树是n( n > = 0 n>=0 n>=0)个结点的有限集合,n = 0时称为空树。在任何一棵非空树中应满足:
- 有且仅有一个特定的称为根的结点。
- 当 n > = 1 n>=1 n>=1时,其余结点可分为m(m>0)个互不相交的有限集合,其中每个集合本身又是一棵树,并且称为根节点的子树。
显然 ,树的定义是递归的,树是一种递归的数据结构。树作为一种逻辑结构,同时又是分层结构,具有以下两个特点:
- 树的根节点没有前驱结点,除根节点以外的所有结点有且仅有一个前驱结点。
- 树中所有结点可以有0个或多个后继结点。
基本术语
- 节点的度:一个节点含有的子树的个数称为该节点的度;
- 树的度:一棵树中,最大的节点的度称为树的度;
- 叶节点或终端节点:度为零的节点;
- 父亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
- 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
- 兄弟节点:具有相同父节点的节点互称为兄弟节点;
- 结点的深度、高度和层次:树中节点的最大层次;
- 堂兄弟节点:父节点在同一层的节点互为堂兄弟;
- 祖先结点:从根到该节点所经分支上的所有节点;
- 子孙结点:以某节点为根的子树中任一节点都称为该节点的子孙。
- 森林:由m(m>=0)棵互不相交的树的集合称为森林;
- 叶子结点:度为0的结点
- 有序树和无序树:树中结点的子树从左到右时有次序的,不能交换,这样的树称为有序树。
- 路径和路径长度:树中两个结点之间的路径是由这两个结点之间所经过的结点序列构成的,路径长度是路径上所经过的边的个数。
树的种类
- 无序树:树中任意节点的子节点之间没有顺序关系,这种树称为无序树,也称为自由树;
- 有序树:树中任意节点的子节点之间有顺序关系,这种树称为有序树;
- 二叉树:每个节点最多含有两个子树的树称为二叉树;
- 完全二叉树:对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树,其中满二叉树的定义是所有叶节点都在最底层的完全二叉树;
- 平衡二叉树(AVL树):当且仅当任何节点的两棵子树的高度差不大于1的二叉树;
- 排序二叉树(二叉查找树(英语:Binary Search Tree),也称二叉搜索树、有序二叉树);
- 哈夫曼树(用于信息编码):带权路径最短的二叉树称为哈夫曼树或最优二叉树;
- B树:一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多余两个子树。
树的存储
顺序存储:将数据结构存储在固定的数组中,然在遍历速度上有一定的优势,但因空间利用率较低(非完全二叉树需要填充0,浪费空间),是非主流二叉树。二叉树通常以链式存储。
链式存储:
由于对节点的个数无法掌握,常见树的存储表示都转换成二叉树进行处理,子节点个数最多为2
常见的一些树的应用场景
- xml,html等,那么编写这些东西的解析器的时候,不可避免用到树
- 路由协议就是使用了树的算法
- mysql数据库索引
- 文件系统的目录结构
- 所以很多经典的AI算法其实都是树搜索,此外机器学习中的decision tree也是树结构
二叉树
- 性质1: 在二叉树的第i层上至多有2^(i-1)个结点(i>0)
- 性质2: 深度为k的二叉树至多有2^k - 1个结点(k>0)
- 性质3: 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;
- 性质4:具有n个结点的完全二叉树的深度必为 log2(n+1)
- 性质5:对完全二叉树,若从上至下、从左至右编号,则编号为i 的结点,其左孩子编号必为2i,其右孩子编号必为2i+1;其双亲的编号必为i/2(i=1 时为根,除外)
完全二叉树和满二叉树
- 完全二叉树——若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。
- 满二叉树——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。
python实现二叉树
创建二叉树并新增结点,按层次遍历的顺序增加
import queue
class Node(object):
"""二叉树结点"""
def __init__(self, elem=-1, lchild=None, rchild=None):
self.elem = elem
self.lchild = lchild
self.rchild = rchild
class BiTree(object):
"""二叉树"""
def __init__(self, root=None):
self.root = root
def add(self, elem):
"""添加结点"""
node = Node(elem)
# 如果树是空的,则对根节点赋值
if self.root == None:
self.root = node
else:
temp_queue = queue.Queue()
temp_queue.put(self.root)
# 对已有的节点进行层次遍历
while temp_queue:
# 弹出队列中第一个结点
cur = temp_queue.get()
if cur.lchild == None:
cur.lchild = node
return
elif cur.rchild ==None:
cur.rchild = node
return
else:
# 如果左右子树都不为空,加入队列继续判断
temp_queue.put(cur.lchild)
temp_queue.put(cur.rchild)
二叉树的遍历
前序遍历
def preorder(bt):
"""树的前序遍历"""
stack = []
stack.append(bt.root)
p = stack.pop()
while p or stack:
if p:
print(p.elem)
stack.append(p)
p = p.lchild
else:
p = stack.pop()
p = p.rchild
注意:python中的None类似于C中的NULL,可以理解为空指针???(不知道对不对)。
- a is not None:不包含空类型
- not a:包含了None和其他空类型
中序遍历
def inorder(bt):
"""二叉树的中序遍历"""
stack = []
stack.append(bt.root)
p = stack.pop()
while p or stack:
if p:
stack.append(p)
p = p.lchild
else:
p = stack.pop()
print(p.elem)
p = p.rchild