1、基本概念
二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)
2、性质(特性)
- 性质1:在二叉树的第 i 层上至多有 2(i-1)个节点(i > 0)
- 性质2:深度为 k 的二叉树至多有 2k-1 个节点 (k > 0)
- 性质3:对于任意一颗二叉树,如果其叶节点树为 N0 ,而度数为 2 的节点总数为 N2,则 N0 = N2 + 1
- 性质4:具有n个节点的完全二叉树的深度必为 log2(n+1)
- 性质5:对完全二叉树,若从上至下、从左至右编号,则编号为 i 的节点,其左孩子编号必为 2i ,其右孩子编号必为 2i + 1 ;其双亲的编号必为 i/2 (i=1 时为根,除外)
(1)完全二叉树:若设二叉树的高度为 h ,除第 h 层外,其他各层(1~h-1)的节点数都达到最大个数,第 h 层有叶子节点,并且叶子节点都是从左到有一次排布。
(2)满二叉树:除了叶节点外每一个节点都有左右叶子且叶子节点都处在最底层的二叉树。
3、二叉树的遍历
树的遍历是树的一种重要的运算。所谓遍历是指对树中所有节点的信息的访问,即依次对树中每个结点访问一次且仅访问一次,我们把这种对所有节点的访问称为 遍历(traversal)。那么树的两种重要的遍历模式是深度优先遍历和广度优先遍历,深度优先一般用递归,广度优先一般用队列。一般情况下能用递归实现的算法大部分也能用堆栈来实现。
3.1、深度优先遍历
对于一颗二叉树,深度优先搜索(Depth First Search)是沿着树的深度遍历树的节点,尽可能深的搜索树的分支。
那么深度遍历有重要的三种方法。这三种方式常被用于访问树的节点,他们之间的不同在于访问每个节点的次序不同。这三种遍历分别叫做 先序遍历(preorder),中序遍历(inorder)和后序遍历(postorder)。下面来依次说明。
- 先序遍历:在先序遍历中,我们先访问根节点,然后递归使用先序遍历访问左子树,再递归使用先序遍历访问右子树
根节点 ——> 左子树 ——> 右子树
python代码实现:
def preorder(self, node):
"""先序遍历"""
if node is None:
return
print(node.elem, end=" ")
self.preorder(node.lchild)
self.preorder(node.rchild)
- 中序遍历:在中序遍历中,我们递归使用中序遍历访问左子树,然后访问根节点,最后再递归使用中序遍历访问右子树。
左子树 ——> 根节点 ——> 右子树
python代码实现:
def inorder(self, node):
"""中序遍历"""
if node is None:
return
self.inorder(node.lchild)
print(node.elem, end=" ")
self.inorder(node.rchild)
- 后序遍历:在后序遍历中,我们先递归使用后序遍历访问左子树和右子树,最后访问根节点
左子树 ——> 右子树 ——> 根节点
python代码实现
def postorder(self, node):
"""后序遍历"""
if node is None:
return
self.postorder(node.lchild)
self.postorder(node.rchild)
print(node.elem, end=" ")
3.2、广度优先遍历(层次遍历)
从根的 root 开始,从上到下,从左到有遍历整个树的节点
def breath_travel(self):
"""广度遍历"""
if self.root is None:
return
queue = [self.root]
while queue:
cur_node = queue.pop(0)
# 打印指针的元素
print(cur_node.elem, end=" ")
# 如果左子节点不为空,就进行 添加
if cur_node.lchild is not None:
queue.append(cur_node.lchild)
if cur_node.rchild is not None:
queue.append(cur_node.rchild)
4、完整代码
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:
self.root = node
return
queue = [self.root]
while queue:
# 节点指针,从第一个开始
cur_node = queue.pop(0)
# 如果左节点的子节点为None,就赋值给他;否则 末尾追加
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 breath_travel(self):
"""广度遍历"""
if self.root is None:
return
queue = [self.root]
while queue:
cur_node = queue.pop(0)
# 打印指针的元素
print(cur_node.elem, end=" ")
# 如果左子节点不为空,就进行 添加
if cur_node.lchild is not None:
queue.append(cur_node.lchild)
if cur_node.rchild is not None:
queue.append(cur_node.rchild)
def preorder(self, node):
"""先序遍历"""
if node is None:
return
print(node.elem, end=" ")
self.preorder(node.lchild)
self.preorder(node.rchild)
def inorder(self, node):
"""中序遍历"""
if node is None:
return
self.inorder(node.lchild)
print(node.elem, end=" ")
self.inorder(node.rchild)
def postorder(self, node):
"""后序遍历"""
if node is None:
return
self.postorder(node.lchild)
self.postorder(node.rchild)
print(node.elem, end=" ")
if __name__ == '__main__':
tree = Tree()
tree.add(0)
tree.add(1)
tree.add(2)
tree.add(3)
tree.add(4)
tree.add(5)
tree.add(6)
tree.add(7)
tree.add(8)
tree.add(9)
tree.breath_travel()
print(" ")
tree.preorder(tree.root)
print(" ")
tree.inorder(tree.root)
print(" ")
tree.postorder(tree.root)
输出结果:
0 1 2 3 4 5 6 7 8 9
0 1 3 7 8 4 9 2 5 6
7 3 8 1 9 4 0 5 2 6
7 8 3 9 4 1 5 6 2 0