- 树结构:树结构是一种描述非线性层次关系的数据结构。
-
树是n个数据结点的集合,在该集合中包含一个根结点,根结点之下分布着一些互不交叉的子集合,这些子集合是根结点的子树。
-
树结构的基本特征:
(1)在一个树结构中,有且只有一个结点没有直接前驱,这个结点就是树的根结点;
(2)除根结点之外,其余每个节点有且仅有一个直接前驱;
(3)每个结点可以有任意多个直接后继。
- 树的概念
- 父结点和子结点:每个结点子树的根称为该结点的子结点,相应的,该结点称为其子结点的父结点;
- 兄弟结点:具有同一父结点的结点称为兄弟结点;
- 结点的度:一个结点所包含子树的数量;
- 树的度:是指该树所有结点中最大的度;
- 叶结点:树中度为零的结点称为叶结点或终端结点;
- 分支结点:树中度不为零的结点称为分支结点或非终端结点;
- 结点的层数:结点的层数从树根开始计算,根结点为第1层、依次向下为第2、3、…n层(树是一种层次结构,每个结点都处在一 定的层次上);
- 树的深度:树中结点的最大层数称为树的深度;
- 有序树:若树中各结点的子树(兄弟结点)是按一 定次序从左向右排列的,称为有序树;
- 无序树:若树中各结点的子树(兄弟结点)未按一 定次序排列,称为无序树;
- 森林(forest): n (n>0)棵互不相交的树的集合。
- 二叉树
-
二叉树是树结构的一种特殊形式,是n个结点的集合,每个结点最多可以有两个结点。
-
二叉树的子树依然是二叉树。
-
二叉树的一个结点上对应的两个子树分别称为左子树和右子树。由于二叉树子树有左右之分,故而二叉树是有序树。
-
普通树结构中,结点的最大度数没有限制,而二叉树结点的最大度数为2。
-
一个二叉树结构也可以是空,此时空二叉树中没有数据结点,是一个空集合。
-
二叉树有两种特殊类型:
(1) 满二叉树:在二叉树中除了最下一层的叶结点外,每层的结点都有两个子结点
(2) 完全二叉树:
二叉树中除最后一层外,其它各层的结点都达到最大个数,且最后一层叶结点按照左往右的顺序连续存在,只缺最后一层右侧若干结点。
- 完全二叉树的性质
二叉树中树结构研究的重点是完全二叉树。对于完全二叉树,若树中包含n个结点,假设这些结点按照顺序方式存储。那么,对于任意一个结点m来说,具有如下性质:
- 如果m!=1,则结点m的父结点的编号为m/2;
- 如果2m≤n,则结点m的左子树根结点的编号为2m; 若2*m>n, 则无左子树,也没有右子树
- 如果2m+1≤n, 则结点m的右子树根结点编号为2m+1;若2*m+1>n,
则无右子树。另外,对于该完全二叉树来说,其深度为[log2n]+1。
这些基本性质展示了完全二叉树结构的特点,在完全二叉树的存储方式及运算处理上都有重要意义。
- 遍历二叉树
遍历二叉树即逐个查找二叉树中的所有结点,这是二叉树的基本操作,因为很多操作都需要首先遍历整个二叉树。由于二叉树结构的特殊性,往往可以采用多种方法进行遍历。
由于二叉树代表的是一种层次结构,因此,首先按层来遍历整个二叉树。对于二叉树的按层遍历,一般不能使用递归算法来编写代码,而是使用一个循环队列来进行处理。
当然也可以使用递归算法来简化遍历算法。一般可以采用以下几种方法来遍历整个二叉树:
- 先序遍历:即先访问根结点,再按先序遍历左子树,最后先序遍历右子树。
1、首先先定义一个树节点类信息,如下:
package com.dz.demo.algorithm;
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x;
}
}
2、递归调用比较简单,具体如下:
private static void preOrderRecursive(TreeNode root) {
if (root == null) return;
System.out.println(root.val);
preOrderRecursive(root.left);
preOrderRecursive(root.right);
}
3、非递归的方法主要是借助栈结构来完成:
private static void preOrderUnRecursive(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.empty(