what's the 树
在了解二叉树之前,首先我们得有树的概念。
树是一种数据结构又可称为树状图,如文档的目录、HTML的文档树都是树结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
-
- 每个节点有零个或多个子节点;
-
- 没有父节点的节点称为根节点;
-
- 每一个非根节点有且只有一个父节点;
-
- 除了根节点外,每个子节点可以分为多个不相交的子树;
有关树的一些相关术语:
-
- 节点的度:一个节点含有的子树的个数称为该节点的度;
-
- 叶节点或终端节点:度为0的节点称为叶节点;
-
- 非终端节点或分支节点:度不为0的节点;
-
- 双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
-
- 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
-
- 兄弟节点:具有相同父节点的节点互称为兄弟节点;
-
- 树的度:一棵树中,最大的节点的度称为树的度;
-
- 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
-
- 树的高度或深度:树中节点的最大层次;
-
- 堂兄弟节点:双亲在同一层的节点互为堂兄弟;
-
- 节点的祖先:从根到该节点所经分支上的所有节点;
-
- 森林:由m(m>=0)棵互不相交的树的集合称为森林;
树的种类有:无序树、有序树、二叉树、霍夫曼树。其中最重要应用最多的就是二叉树,下面我们来学习有关二叉树的知识。
二叉树
二叉树的定义为度不超过2的树,即每个节点最多有两个叉(两个分支)。上面那个例图其实就是一颗二叉树。
二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。
二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有2^{i-1}个结点;深度为k的二叉树至多有2^k-1个结点;对任何一棵二叉树T,如果其终端结点数为n_0,度为2的结点数为n_2,则n_0=n_2+1。
一棵深度为k,且有2^k-1个节点的二叉树,称为满二叉树。这种树的特点是每一层上的节点数都是最大节点数。而在一棵二叉树中,除最后一层外,若其余层都是满的,并且最后一层或者是满的,或者是在右边缺少连续若干节点,则此二叉树为完全二叉树。具有n个节点的完全二叉树的深度为log2n+1。深度为k的完全二叉树,至少有2^(k-1)个节点,至多有2^k-1个节点。
二叉树的存储方式分为链式存储和顺序存储(类似列表)两种
二叉树父节点下标i和左孩子节点的编号下标的关系为2i+1,和右孩子节点的编号下标的关系为2i+2
二叉树有两个特殊的形态:满二叉树和完全二叉树
满二叉树
一个二叉树,如果除了叶子节点外每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。
完全二叉树
叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树为完全二叉树。即右边的最下层和次下层可以适当缺一个右子数
完全二叉树是效率很高的数据结构
二叉树的遍历
二叉树的链式存储:将二叉树的节点定义为一个对象,节点之间通过类似链表的链接方式来连接。
二叉树结点的定义
#二叉树结点的定义 class BiTreeNode: def __init__(self, data): self.data = data self.lchild = None self.rchild = None
二叉树的遍历分为四种——前序遍历、中序遍历、后序遍历和层级遍历
设树结构为:
- 前序遍历:先打印根,再递归其左子树,后递归其右子数 E ACBD GF
- 中序遍历:以根为中心,左边打印左子树,右边打印右子树(注意,每个子树也有相应的根和子树) A BCD E GF
- 后序遍历:先递归左子树,再递归右子树,后打印根(注意,每个子树也有相应的根和子树BDC A FG E
- 层次遍历:从根开始一层一层来,同一层的从左到右输出E AG CF BD
四种遍历方法的代码实现:
from collections import deque #结点的定义 class BiTreeNode: def __init__(self, data): self.data = data self.lchild = None self.rchild = None #二叉树结点 a = BiTreeNode('A') b = BiTreeNode('B') c = BiTreeNode('C') d = BiTreeNode('D') e = BiTreeNode('E') f = BiTreeNode('F') g = BiTreeNode('G') #结点之间的关系 e.lchild = a e.rchild = g a.rchild = c c.lchild = b c.rchild = d g.rchild = f root = e #前序遍历:先打印根,再递归左孩子,后递归右孩子 def pre_order(root): if root: print(root.data, end='') pre_order(root.lchild) pre_order(root.rchild) #中序遍历:以根为中心,左边打印左子树,右边打印右子树(注意,每个子树也有相应的根和子树) #(ACBD) E (GF)-->(A(CBD)) E (GF)-->(A (B C D)) E (G F) def in_order(root): if root: in_order(root.lchild) print(root.data, end='') in_order(root.rchild) #后序遍历:先递归左子树,再递归右子数,后打印根(注意,每个子树也有相应的根和子树) # (ABCD)(GF)E-->((BCD)A)(GF)E-->(BDCA)(FG)E def post_order(root): if root: post_order(root.lchild) post_order(root.rchild) print(root.data, end='') #层次遍历:一层一层来,同一层的从左到右输出 def level_order(root): queue = deque() queue.append(root) while len(queue) > 0: node = queue.popleft() print(node.data,end='') if node.lchild: queue.append(node.lchild) if node.rchild: queue.append(node.rchild) pre_order(root)#EACBDGF print("") in_order(root)#ABCDEGF print("") post_order(root)#BDCAFGE print("") level_order(root)#EAGCFBD