6 树与树的算法
基本知识
特点
- 每个节点都有0/多个 子节点
- 没有父节点的节点 被称为 根节点
- 每一个非根节点 有且只有 一个 父节点
- 除了根节点外,每个子节点可分为多个不相交的子树
树的术语
-
节点的度:一个节点含有的子树的个数
-
树的度:一棵树中,最大的节点的度
-
叶节点/终端节点:度为0的节点(不含子节点)
-
父节点:含有子节点的节点
-
子节点:含有子树的根节点
-
兄弟节点:具有相同的父节点
-
节点的层次:从根开始定义,根为第一层;根的子节点为第二层
-
树的高度/深度:树节点的最大层次
-
堂兄弟节点:父节点在同一层的节点
-
节点的祖先:从根到该节点所经历分支上的所有节点
-
子孙:以某节点为根的子树中任一节点
-
森林:由m m>=0 棵 互不相交的树的集合
树的种类
- 无序树/自由树:没有研究价值
- 有序树:二叉树等
树的存储
- 源序存储:有速度优势 但是所占空间大,非主流
- 链式存储:主流
应用场景
- HTML
- 路由协议
- 数据库
- 文件系统的目录结构
- 经典的AI算法
6.1 二叉树
左子树 右子树
二叉树的性质
-
在二叉树的第i层上至多有2^(i-1)个结点(i>0)
-
深度为k的二叉树至多有2^k - 1个结点(k>0)
-
对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;
-
具有n个结点的完全二叉树的深度必为 log2(n+1) <和性质2互逆>
-
对完全二叉树,若从上至下、从左至右编号,则编号为i 的结点:
其左孩子编号必为2i,其右孩子编号必为2i+1;其双亲的编号必为i/2(i=1 时为根,除外)
树的实现及遍历
对链表的扩充
深度优先一般用递归,广度优先一般用队列
二叉树的广度优先遍历
class Node(object):
'''节点'''
def __init__(self,item):
self.item = item
self.lchild = None
self.rchild = None
class Tree(object):
'''二叉树'''
def __init__(self):
self.root = None
def Add(self,item):
''''追加元素'''
node = None(item)
# 对根节点:queue=None
if self.root is None:
self.root = node
return
queue = [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)
def breadth_travel(self):
''''广度遍历 '''
if self.root is None:
return
queue = [self.root]
while queue:
cur.node = queue.pop(0)
print(cur_node.item)
if cur.node.lchild is None:
cur.node.lchild = node
if cur.node.rchild is None:
cur.node.rchild = node
深度遍历
先中后根据 根 的位置
- 先序遍历
根 左 右
一棵一棵树处理完
def preorder(self, root):
"""递归实现先序遍历"""
if root == None:
return
print root.elem
self.preorder(root.lchild)
self.preorder(root.rchild)
- 中序遍历
左 根 右
def inorder(self, root):
"""递归实现中序遍历"""
if root == None:
return
self.inorder(root.lchild)
print root.elem
self.inorder(root.rchild)
- 后序遍历
左 右 根
def postorder(self, root):
"""递归实现后续遍历"""
if root == None:
return
self.postorder(root.lchild)
self.postorder(root.rchild)
print root.elem
小练习
由先+中/后+中画出树
中序把树左右分开