树(英语: tree)是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由 n ( n > = 1 ) n(n>=1) n(n>=1) 个有限节点组成一个具有层次关系的集合。把它叫做“树""是因为看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
比如说:
和父节点是反着来着,就不画图了。
就是最大的节点层次(根节点是第一层)。
将数据结构存储在固定的数组中,然在遍历速度上有一定的优势,但因所占空间比较大,是非主流二叉树。二叉树通常以链式存储。
树长成什么样子,我们就在构造的时候将其构造为什么样子。
由于对节点的个数无法掌握,常见树的存储表示都转换成二叉树进行处理,子节点个数最多为2。
xml
,html
等,那么编写这些东西的解析器的时候,不可避免用到树二叉树是每个节点最多有两个子树的树结构。
通常子树被称作"左子树"(left subtree)和"右子树" (right subtree)。
完全二叉树——若设二叉树的高度为 h h h,除第 h h h 层外,其它各层 ( 1 h − 1 ) (1~h-1) (1 h−1) 的结点数都达到最大个数,第 h h h 层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。
满二叉树——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。
当且仅当任何节点的两棵子树的高度差不大于1(超过2 -> l e 1 le 1 le1)的二叉树;
可以看到,平衡二叉树级别没有完全二叉树和满二叉树那么高,它描述的是一种性质。
- 满二叉树和完全二叉树也可以是平衡二叉树
- 平衡二叉树也可以是完全二叉树和满二叉树
排序二叉树的结构规则很简单,只遵循一个基本规则:
因为这个树非常有规律,且顺序排好了,因此在查找的时候非常方便,过程和二分查找类似。
假设我们需要找39
这个数是否存在:
39
比40
小,在40
的左边39
比18
大,在18
的右边39
比37
大,在37
的右边39
,返回True
通过使用Node类,定义三个属性:
class Node():
"""树的节点"""
def __init__(self, item):
self.elem = item # 需要存储的数
self.lchild = None # 左子节点
self.rchild = None # 右子节点
通过使用Tree类,定义根节点即可。
class Tree():
"""二叉树"""
def __init__(self):
self.root = None # 二叉树的根节点
# coding: utf-8
class Node():
"""树的节点"""
def __init__(self, item):
self.elem = item # 需要存储的数
self.lchild = None # 左子节点
self.rchild = None # 右子节点
class Tree():
"""二叉树"""
def __init__(self):
self.root = None # 二叉树的根节点
def add(self, item):
node = Node(item) # 先构造一个节点
if self.root is None: # 首先判断树是否为空树,如果是空树,则将node链接到root节点即可
self.root = node
return
# 当树不是空树时,开始使用队列进行判断空位置,将新的node链接到空位置上
queue = [] # 创建一个队列(用来存放需要处理的元素)
queue.append(self.root) # 将根节点添加到队列中 这两句话可以简写为 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)
树的遍历是树的一种重要的运算。所谓遍历是指对树中所有节点的信息的访问,即依次对树中每个节点访问一次且仅访问一次,我们把这种对所有节点的访问称为遍历(traversal)。
那么树的两种重要的遍历模式:
一般情况下能用递归实现的算大部分也能用堆栈来实现。
对于一颗二叉树,深度优先搜索(Depth First Search)是沿着树的深度遍历树的节点,尽可能深的搜索树的分支。那么深度遍历有重要的三种方法,这三种方式常被用于访问树的节点,它们之间的不同在于访问每个节点的次序不同。这三种遍历分别叫做:
我们来给出它们的详细定义,然后举例看看它们的应用。
在先序遍历中,我们先访问根节点 (root),然后递归使用先序遍历访问左子树,再递归使用先序遍历访问右子树
代码实现:
def preorder(self, root):
"""
使用递归的方式实现先序遍历
因为设计到了递归,所以我们需要将根和子根传给自身,故有一个root形参
根 → 左 → 右
"""
if root is None: # 递归的终止条件
return
print(root.elem, end=", ")
self.preorder(root.lchild)
self.preorder(root.rchild)
中序遍历在中序遍历中,我们递归使用中序遍历访问左子树,然后访问根节点,最后再递归使用中序遍历访问右子树
左子树 → 根节点 → 右子树
代码实现:
def inorder(self, root):
"""
使用递归的方式实现中序遍历
因为设计到了递归,所以我们需要将根和子根传给自身,故有一个root形参
左 → 根 → 右
"""
if root is None: # 递归的终止条件
return
self.inorder(root.lchild)
print(root.elem, end=", ")
self.inorder(root.rchild)
后序遍历在后序遍历中,我们先递归使用后序遍历访问左子树和右子树,最后访问根节点
左子树 → 右子树 → 根节点
代码实现:
def postorder(self, root):
"""
使用递归的方式实现后序遍历
因为设计到了递归,所以我们需要将根和子根传给自身,故有一个root形参
左 → 右 → 根
"""
if root is None: # 递归的终止条件
return
self.postorder(root.lchild)
self.postorder(root.rchild)
print(root.elem, end=", ")
从树的root开始,从上到下、从左到右遍历整棵树的节点。
def breadth_travel(self):
"""广度遍历 (思路和add是一样的)
特殊情况:一开始就是一个空树
"""
if self.root is None:
return
queue = [self.root] # 创建队列并且添加root节点
print("[", end="")
while queue: # 只要队列不为空,则一直执行
cur_node = queue.pop(0)
print(cur_node.elem, end=", ") # 打印当前节点的元素
if cur_node.lchild: # 左节点存在
queue.append(cur_node.lchild) # 添加到队列中等待处理
if cur_node.rchild:
queue.append(cur_node.rchild) # 添加到队列中等待处理
print("]", end="\n")
后序:左 右 根
中序:左 根 右
后序结果:7839415620
中序结果:7381940526
首先根据后序的结果我们可以推出root
中序结果:7381940526
:738194 0 526
用738194去匹配后序结果中的数字783941,然后找到根1
738194 0 526
738 1 94 0 526
对于738来说,匹配后序结果中的数字783,找到root 3
7 3 8 1 94 0 526
7 3 8已经确定了(根据中序顺序)
我们再看一下94,让它去匹配后序结果中的数字94. root为4
7 3 8 1 9 4 0 526
左 根 右(没有右)
所以94也确定了(左根右)
现在,root 0的左边部分处理完毕了,我们再看右边部分的526
让它去匹配后序结果中的562,根为2
7 3 8 1 9 4 0 5 2 6
正好符合中序顺序(左 根 右)
所以二叉树就确定了(结果和上图是一样的)
:738194 0 526
用738194去匹配后序结果中的数字783941,然后找到根1
738194 0 526
738 1 94 0 526
对于738来说,匹配后序结果中的数字783,找到root 3
7 3 8 1 94 0 526
7 3 8已经确定了(根据中序顺序)
我们再看一下94,让它去匹配后序结果中的数字94. root为4
7 3 8 1 9 4 0 526
左 根 右(没有右)
所以94也确定了(左根右)
现在,root 0的左边部分处理完毕了,我们再看右边部分的526
让它去匹配后序结果中的562,根为2
7 3 8 1 9 4 0 5 2 6
正好符合中序顺序(左 根 右)
所以二叉树就确定了(结果和上图是一样的)