浅谈数据结构-树
相关文章链接:
观前提示:
本文所使用的IDEA版本为ultimate 2019.1,JDK版本为1.8.0_141。
1 基本概念
-
树:一种数据结构,它是由n(n>=1)个有限结点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
-
结点:每个元素称为结点(node)。
-
根结点:没有父结点的结点称为根结点。
-
结点的度:一个结点含有的子结点的个数称为该结点的度。
-
树的度:一棵树中,最大的结点的度称为树的度。
-
结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推。
-
叶结点:度为0的结点称为叶结点。
-
树的高度或深度:树中结点的最大层次。
2 特点
-
每个结点有零个或多个子结点
-
没有父结点的结点称为根结点
-
每一个非根结点有且只有一个父结点
-
除了根结点外,每个子结点可以分为多个不相交的子树
3 种类
3.1 无序树
树中任意节点的子结点之间没有顺序关系,这种树称为无序树,也称为自由树。
3.2有序树
树中任意节点的子结点之间有顺序关系,这种树称为有序树。
3.3二叉树
每个节点最多含有两个子树的树称为二叉树。
3.4满二叉树
叶节点除外的所有节点均含有两个子树的树被称为满二叉树。
3.5完全二叉树
若设二叉树的深度为h,除第 h 层外,其它各层 (1~(h-1)层) 的结点数都达到最大个数,第h层所有的结点都连续集中在最左边,这就是完全二叉树。
3.6二叉排序树(二叉查找树,也称二叉搜索树)
一棵空树,或者是具有下列性质的二叉树
-
若左子树不空,则左子树上所有结点的值均小于它的根结点的值。
-
若右子树不空,则右子树上所有结点的值均大于它的根结点的值。
-
左、右子树也分别为二叉排序树。
-
没有键值相等的结点。
3.7 哈夫曼树(最优二叉树)
带权路径最短的二叉树称为哈夫曼树或最优二叉树。
3.8 AVL树(高度平衡树)
AVL树是最先发明的自平衡二叉查找树,在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。
3.9 红黑树
红黑树是一种特化的AVL树(平衡二叉树),都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。其特点如下:
-
节点是红色或黑色。
-
根节点是黑色。
-
所有叶子都是黑色。(叶子是NIL节点)
-
每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
-
从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
3.10 B树
一棵m阶B树是一棵平衡的m路搜索树。它或者是空树,或者是满足下列性质的树:
-
根结点至少有两个子女。
-
每个非根节点所包含的关键字个数 j 满足:[m/2] - 1 <= j <= m - 1。
-
除根结点以外的所有结点(不包括叶子结点)的度数正好是关键字总数加1,故内部子树个数 k 满足:[m/2] <= k <= m 。
-
所有的叶子结点都位于同一层。
3.11 B+树
B+树是B树的一种变形形式,B+树上的叶子结点存储关键字以及相应记录的地址,叶子结点以上各层作为索引使用。一棵m阶的B+树定义如下:
-
每个结点至多有m个子女。
-
除根结点外,每个结点至少有[m/2]个子女,根结点至少有两个子女。
-
有k个子女的结点必有k个关键字。
3.12 B* 树
B* 树是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针;B* 树定义了非叶子结点关键字个数至少为(2/3)* M,即块的最低使用率为2/3(代替B+树的1/2)。
B* 树的分裂:当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了);如果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针。
4.遍历
这里仅仅介绍二叉树的遍历,包括先序遍历、中序遍历、后序遍历、层次遍历。
(借用网上随便找的一张二叉树的图讲解)
4.1 先序遍历
根结点->遍历左子树->遍历右子树(根->左->右)
顺序:根节点->前序遍历左子树->前序遍历右子树
结果为:ABCDEF
4.2 中序遍历
遍历左子树->根结点->遍历右子树(左->根->右)
顺序:中序遍历左子树->根节点->中序遍历右子树
结果为:CBDAEF
4.3 后序遍历
遍历左子树->遍历右子树->根结点(左->右->根)
顺序:后序遍历左子树->后序遍历右子树->根节点
结果为:CDBFEA
4.4 层次遍历
按层次从左到右遍历
结果为:ABECDF
4.5 java代码实例
自定义节点TreeNode.java
package TestTree;
/**
* 自定义树节点
* @author jjy
* @date 2020-10-14
*/
public class TreeNode {
private String root;
private TreeNode left;
private TreeNode right;
public TreeNode() {
}
public TreeNode(String root) {
this.root = root;
}
public String getRoot() {
return root;
}
public void setRoot(String root) {
this.root = root;
}
public TreeNode getLeft() {
return left;
}
public void setLeft(TreeNode left) {
this.left = left;
}
public TreeNode getRight() {
return right;
}
public void setRight(TreeNode right) {
this.right = right;
}
}
工具类TraversalUtil.java
package TestTree;
import java.util.ArrayList;
import java.util.List;
/**
* 二叉树遍历Util
* @author jjy
* @date 2020-10-14
*/
public class TraversalUtil {
private static List<String> list;
public static void init(){
list = new ArrayList<>();
}
/**
* 先序遍历
* @param node
* @return
*/
public static List<String> preOrder(TreeNode node){
if(node != null){
list.add(node.getRoot());
preOrder(node.getLeft());
preOrder(node.getRight());
}
return list;
}
/**
* 中序遍历
* @param node
* @return
*/
public static List<String> inOrder(TreeNode node){
if(node != null){
inOrder(node.getLeft());
list.add(node.getRoot());
inOrder(node.getRight());
}
return list;
}
/**
* 后序遍历
* @param node
* @return
*/
public static List<String> postOrder(TreeNode node){
if(node != null){
postOrder(node.getLeft());
postOrder(node.getRight());
list.add(node.getRoot());
}
return list;
}
}
测试类TestTraversal.java
package TestTree;
import java.util.List;
public class TestTraversal {
public static void main(String[] args) {
TreeNode t1 = new TreeNode("A");
TreeNode t2 = new TreeNode("B");
TreeNode t3 = new TreeNode("E");
TreeNode t4 = new TreeNode("C");
TreeNode t5 = new TreeNode("D");
TreeNode t6 = new TreeNode("F");
t2.setLeft(t4);
t2.setRight(t5);
t3.setRight(t6);
t1.setLeft(t2);
t1.setRight(t3);
TraversalUtil.init();
List<String> preList = TraversalUtil.preOrder(t1);
System.out.println("先序排列结果为:");
preList.forEach(System.out::print);
TraversalUtil.init();
List<String> inList = TraversalUtil.inOrder(t1);
System.out.println("\n中序排列结果为:");
inList.forEach(System.out::print);
TraversalUtil.init();
List<String> postList = TraversalUtil.postOrder(t1);
System.out.println("\n后序排列结果为:");
postList.forEach(System.out::print);
}
}
结果为