基础知识
树(Tree)是一种非线性结构,用来模拟具有树状结构性质的数据集合。它是由n(n>0)个有限结点组成一个具有层次关系的集合。树是递归结构,在树的定义中又用到了树的概念。
基本术语
- 树结点:包含一个数据元素及若干指向子树的分支;
- 孩子结点(子结点):结点的子树的根称为该结点的孩子;
- 双亲结点(父结点):B结点是A结点的孩子,则A结点是B结点的双亲;
- 兄弟结点:同一双亲的孩子结点;
- 堂兄结点:同一层上结点;
- 结点层次:根结点的层定义为1;根的孩子为第二层结点,依此类推;
- 树的高(深)度:树中最大的结点层;
- 结点的度:结点子树的个数;
- 树的度:树中最大的结点度;
- 叶子结点:也叫终端结点,是度为0的结点;
- 分枝结点:度不为0的结点(非终端结点);
- 森林:互不相交的树集合;
有序树:子树有序的树,如:家族树; 无序树:不考虑子树的顺序
性质
- 每个节点有零个或多个子节点;
- 没有父节点的节点称为根节点;
- 每一个非根节点有且只有一个父节点;
- 除了根节点外,每个子节点可以分为多个不相交的子树;
- 树里面没有环路(cycle)。
二叉树
二叉树(Binary tree)是每个节点最多只有两个分支(即不存在分支度大于2的节点)的树结构。通常分支被称作“左子树”或“右子树”。二叉树的分支具有左右次序,不能随意颠倒。二叉树可以为空。
二叉树的遍历
深度优先遍历(Depth First Search,DFS):沿着树的深度遍历树的节点,尽可能深的搜索树的分支。
- 前序遍历:NLR,根结点->左子树->右子树;
- 中序遍历:LNR,左子树->根结点->右子树;
- 后续遍历:LRN,左子树->右子树->根结点。
广度优先遍历(Breadth First Search,BFS;也叫层次遍历,level order):是从根节点开始,沿着树的宽度遍历树的节点。如果所有节点均被访问,则算法中止。
Python建立二叉树及实现各种遍历方法
二叉树结点类
class BinaryNode(object):
"""二叉树节点类"""
def __init__(self, item, left=None, right=None):
self.item = item
self.left = left
self.right = right
二叉树类
其中包含二叉树的常用操作和七种遍历方法。不做要求的情况下,使用递归的方式实现遍历最简单。
class BinaryTree(object):
"""二叉树及几种遍历方法"""
def __init__(self):
self.root = None
self.bi_list = [] # 使用队列存放二叉树的层次节点信息
def is_empty(self):
return self.root is None
def add(self, item):
"""依次为二叉树添加节点(从上至下,从左至右)"""
node = BinaryNode(item)
if self.is_empty():
self.root = node
self.bi_list.append(node)
else:
tmp_node = self.bi_list[0]
if tmp_node.left is None:
tmp_node.left = node
self.bi_list.append(node)
else:
tmp_node.right = node
self.bi_list.append(node)
self.bi_list.pop(0)
def pre_order(self, root):
if root is None:
return
print(root.item)
self.pre_order(root.left)
self.pre_order(root.right)
def in_order(self, root):
if root is None:
return
self.in_order(root.left)
print(root.item)
self.in_order(root.right)
def post_order(self, root):
if root is None:
return
self.post_order(root.left)
self.post_order(root.right)
print(root.item)
def level_order(self, root):
"""层次遍历,也叫广度优先搜索(Breadth-first search)。
采用队列实现。"""
if root is None:
return
queue = []
queue.append(root)
while queue:
node = queue.pop(0)
print(node.item)
# 如果孩子节点不为空,那么从左至右添加进来
if node.left is not None:
queue.append(node.left)
if node.right is not None:
queue.append(node.right)
def pre_order_stack(self, root):
"""先序遍历的非递归实现。
使用栈"""
if root is None:
return
stack = []
node = root
while node or stack:
while node:
print(node.item)
stack.append(node)
node = node.left
node = stack.pop()
node = node.right
def in_order_stack(self, root):
"""中序遍历的非递归实现。
使用栈"""
if root is None:
return
stack = []
node = root
while node or stack:
while node:
stack.append(node)
node = node.left
node = stack.pop()
print(node.item)
node = node.right
def post_order_stack(self, root):
"""后序遍历的非递归实现。
使用栈"""
if root is None:
return
stack1, stack2 = [], []
stack1.append(root)
while stack1:
# 此循环为了找出后续遍历的逆序,存入stack2中
# 先将当前节点拿出来存入stack2中
node = stack1.pop()
stack2.append(node)
if node.left is not None:
# 若左孩子非空,先入栈stack1(先入后出,所以是逆序)
stack1.append(node.left)
if node.right is not None:
# 若右孩子非空,入栈stack1
stack1.append(node.right)
for i in stack2[::-1]:
print(i.item)
def height(self, root):
"""递归的求二叉树的最大高度。从叶子结点为‘0层’依次往上数"""
if root is None:
return 0
lheight = self.height(root.left)
rheight = self.height(root.right)
return max(lheight + 1, rheight + 1)
def test():
BT = BinaryTree()
print('二叉树是否为空:', BT.is_empty())
print('依次为二叉树添加10个节点...')
for i in range(10):
BT.add(i + 1)
print('前序遍历')
BT.pre_order(BT.root)
print('中序遍历')
BT.in_order(BT.root)
print('后序遍历')
BT.post_order(BT.root)
print('前序遍历,非递归方式')
BT.pre_order_stack(BT.root)
print('中序遍历,非递归方式')
BT.in_order_stack(BT.root)
print('后序遍历,非递归方式')
BT.post_order_stack(BT.root)
print('层次遍历')
BT.level_order(BT.root)
print('二叉树的最大高度:', BT.height(BT.root))
二叉搜索树
二叉搜索树是一种节点值之间具有一定数量级次序的二叉树,对于树中每个节点:
- 若其左子树存在,则其左子树中每个节点的值都不大于该节点值;
- 若其右子树存在,则其右子树中每个节点的值都不小于该节点值。
树节点定义
# tree node definition
class Node(object):
def __init__(self, value, lchild=None, rchild=None):
self.value = value
self.lchild = lchild
self.rchild = rchild
树定义
# tree definition
class Tree(object):
def __init__(self, root=None):
self.root = root
# node in-order traversal(LDR)
def traversal(self):
traversal(self.root)
# insert node
def insert(self, value):
self.root = insert(self.root, value)
# delete node
def delete(self, value):
self.root = delete(self.root, value)
模块中对树结构中的函数进行实现
# node in-order traversal(LDR)
def traversal(node):
if not node:
return
traversal(node.lchild)
print(node.value,end=' ')
traversal(node.rchild)
# insert node
def insert(root, value):
if not root:
return Node(value)
if value < root.value:
root.lchild = insert(root.lchild, value)
elif value > root.value:
root.rchild = insert(root.rchild, value)
return root
# delete node
def delete(root, value):
if not root:
return None
if value < root.value:
root.lchild = delete(root.lchild, value)
elif value > root.value:
root.rchild = delete(root.rchild, value)
else:
if root.lchild and root.rchild: # degree of the node is 2
target = root.lchild # find the maximum node of the left subtree
while target.rchild:
target = target.rchild
root = delete(root, target.value)
root.value = target.value
else: # degree of the node is [0|1]
root = root.lchild if root.lchild else root.rchild
return root
测试代码与输出
if __name__ == '__main__':
arr = [5, 3, 4, 0, 2, 1, 8, 6, 9, 7]
T = Tree()
for i in arr:
T.insert(i)
print('BST in-order traversal------------------')
T.traversal()
print('\ndelete test------------------')
for i in arr[::-1]:
print('after delete',i,end=',BST in-order is = ')
T.delete(i)
T.traversal()
print()
输出结果为
BST in-order traversal------------------
0 1 2 3 4 5 6 7 8 9
delete test------------------
after delete 7,BST in-order is = 0 1 2 3 4 5 6 8 9
after delete 9,BST in-order is = 0 1 2 3 4 5 6 8
after delete 6,BST in-order is = 0 1 2 3 4 5 8
after delete 8,BST in-order is = 0 1 2 3 4 5
after delete 1,BST in-order is = 0 2 3 4 5
after delete 2,BST in-order is = 0 3 4 5
after delete 0,BST in-order is = 3 4 5
after delete 4,BST in-order is = 3 5
after delete 3,BST in-order is = 5
after delete 5,BST in-order is =
平衡二叉树
平衡二叉树的提出就是为了保证树不至于太倾斜,尽量保证两边平衡。因此它的定义如下:
- 平衡二叉树要么是一棵空树
- 要么保证左右子树的高度之差不大于 1
- 子树也必须是一颗平衡二叉树
也就是说,树的两个左子树的高度差别不会太大。