1.树的结构
1.1树的概念
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
(1)有一个特殊的结点,称为根结点,根结点没有前驱结点
(2)除根结点外,其余结点被分成M(M > 0)个互不相交的集合T1、T2、…、Tm,其中每一个集 合Ti (1 <= i <=m) 又是一棵与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个()或多 个后继
(3)树是递归定义的。
1.2有关树的概念
- 结点的度:一个结点含有子树的个数称为该结点的度; 如上图:A的度为6
- 树的度:一棵树中,所有结点度的最大值称为树的度; 如上图:树的度为6
- 叶子结点或终端结点:度为0的结点称为叶结点; 如上图:B、C、H、I…等节点为叶结点
- 双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点; 如上图:A是B的父结点
- 孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点; 如上图:B是A的孩子结点
- 根结点:一棵树中,没有双亲结点的结点;如上图:A
- 结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推
- 树的高度或深度:树中结点的最大层次; 如上图:树的高度为4
- 非终端结点或分支结点:度不为0的结点; 如上图:D、E、F、G…等节点为分支结点
- 兄弟结点:具有相同父结点的结点互称为兄弟结点; 如上图:B、C是兄弟结点
- 堂兄弟结点:双亲在同一层的结点互为堂兄弟;如上图:H、I互为兄弟结点
- 结点的祖先:从根到该结点所经分支上的所有结点;如上图:A是所有结点的祖先
- 子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙
- 森林:由m(m>=0)棵互不相交的树组成的集合称为森林
2.二叉树
2.1概念
一棵二叉树是结点的一个有限集合,该集合:
- 或者为空
- 或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。
从上图可以看出: - 二叉树不存在度大于2的结点
- 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
3.对二叉树的操作
1.自定义一个二叉树。
对于一个二叉树的结构,包含左节点的引用,以及右节点的引用,以及存储的key值,我们做出如下代码定义一个二叉树
public class MyTree {
static class myTree{
public myTree left;
public myTree right;
public int val;
public myTree(int val) {
this.val = val;
}
}
public myTree root;
}
如上代码,定义了一个可以存储int类型数据的二叉树,并提供了一个构造方法,用来初始化树。此时呢树还缺少其他操作,例如插入操作,删除操作,获取操作等。我们一个一个进行分析。
4.二叉树的遍历
4.1前序遍历
对于前序遍历其思想我们可以看下图:
(1)B,C相当于A节点的左右子树
(2)前序遍历的思路就是先打印根节点,在打印左节点,最后打印右节点
那么我的思路就是,先打印A节点后,我们去打印左子树,左子树也要满足前序遍历思想,此时,应该打印B,
B打印完成后,打印其左节点,再去打印其右节点,那么此时,根节点和左树打印完成,打印右树,右树同样得满足前序遍历规则,先打印C,在打印F,最后打印G。
根据上述得思路我们可以得到以下的代码:
//前序遍历
public void preOrder(myTree root){
if (root == null){
return;
}
System.out.println(root.val + " ");
preOrder(root.left);
preOrder(root.right);
}
4.2中序遍历
中序遍历的思想就是,先打印左子树,在打印根节点,最后打印右子树,只有当左子树打印完成时才可以打印根节点,这就使得打印出的二叉树顺序,根节点在左子树和右子树的中间,一下就是中序遍历。
//中序遍历
public void inOrder(myTree root){
if (root == null){
return;
}
inOrder(root.left);
System.out.println(root.val + " ");
inOrder(root.right);
}
4.3后序遍历
完成了前序遍历和中序遍历,对于后序遍历,我们不难看出,区分这三种打印顺序最主要的是根节点的打印时机,后序遍历打印根节点在最后,也就是造成根节点永远在最后,以下就是后续遍历的代码:
//后序遍历
public void postOrder(myTree root){
if (root == null){
return;
}
postOrder(root.left);
postOrder(root.right);
System.out.println(root.val + " ");
}
4.4层序遍历
层序遍历,我们来看下图:
他的层序遍历结果为:A B C D E F G 。先打印第一层,在打印第二层,以此类推,我门可以借助队列的“先进先出”这一特点来完成层序遍历。
第一步,我们先将A节点放入队列中,我们直接将A出队列
第二步,将A节点的左节点放入队列,再将右节点放入队列,
第三步,将B节点出队列,将B节点的左节点放入队列,右节点放入队列
到了第三步,大家有没有发现,队列中,总是每一层的最左端先出队列,同时将这个节点的左右节点,放到了队尾,当上一层全部出队列,下一层也已经从左至右的存放在队列当中,我们只需要判断队列是否为空,如果不为空继续出队列,
//层序遍历
public List<Integer> levelOrder(myTree root){
List<Integer> list = new ArrayList<>();
if (root == null ){
return list;
}
Queue<myTree> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()){
myTree cur = queue.poll();
list.add(cur.val);
if (cur.left != null){
queue.add(cur.left);
}
if (cur.right != null){
queue.add(cur.right);
}
}
return list;
}