1.树(tree)的概念
注释在线结构:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
树:是n个节点的有限集合。当n=0时,称为空树。在任意一个非空树中,有且仅有一个特定的称为根(root)的节点。当n>1时,其余节点可分为m(m>0)个互不相交的有限集合 T1,T2 ,Tm,其中每一个集合又是一棵树,并且称为根的子树。
节点的度:节点拥有的子树个数称为节点的度。如下图B节点的度为2,J节点的度为1,度为0的节点称为叶子节点或终端节点(拥有一个节点的树),如 A,C,E,K
树的度:节点的度中最大值,为数的度,如下图树度为2
树的深度或高度:节点的层次从根开始定义起,根为第一层,根的孩子为第二层
如下如:
线性结构:
- 第一个数据元素:无前驱。
- 最后一个元素:无后继。
- 中间元素:一个前驱一个后继。
树结构:
- 根节点:无双亲,唯一。
- 叶子节点:无孩子,可以多个。
- 中间节点:一个双亲多个孩子。
2.树的存储结构
2.1 双亲表示法
结构代码定义:
@Data
public class TreeParentDemo {
//根节点
private Node root;
public TreeDemo(String data) {
this.root = new Node(data,null);
}
@Data
class Node{
public Node(String data, Node parent) {
this.data = data;
this.parent = parent;
}
/**
* 数据域 存储A,B,C等
*/
private String data;
/**
* 指向父亲节点的地址
*/
private Node parent;
}
}
注释:根节点的父级地址设置为null,每个节点存储数据域和父级节点地址。
如下图:
数据库存储:
id | data | parentId |
---|---|---|
0 | A | -1 |
1 | B | 0 |
2 | C | 0 |
3 | D | 1 |
4 | E | 2 |
5 | F | 2 |
6 | G | 3 |
7 | H | 3 |
8 | I | 3 |
9 | J | 4 |
优点:1.查找已知节点的父节点很容易,直接通过父级节点地址找到它的双亲。所用时间复杂度为O[1]。
例如:已知F(5)节点 ,查找其父节点,可直接通过F节点中父级地址(id:2),直接查找。
缺点:1.查找已知节点的孩子节点,需要遍历整个数据结构才行.
例如:已知F(5)节点 ,查找其孩子节点,需要遍历正个数据结构,对比查找。如数据库执行 select * from 表 where parentId = 5 需要扫描整个数据行一一对比(该表parentId未加索引的情况下).
2.2 孩子表示发
结构代码定义:
@Data
public class TreeChildDemo {
//根节点
private Node root;
public TreeChildDemo(String data, List<Node> childList) {
this.root = new Node(data,childList);
}
@Data
class Node{
public Node(String data, List<Node> childList) {
this.data = data;
this.childList = childList;
}
/**
* 数据域 存储A,B,C等
*/
private String data;
/**
* 指向孩子节点的地址(可为数组arraylist或单链表linkArralist)(一个双亲可有多个孩子)
*/
private List<Node> childList;
}
注释:双亲节点可有多个孩子,故节点用一个集合来存储,该集合可为LinkArrayList 单链表结构存储,也可为ArrayList 数组结构存储
如图:
数据库存储:
id | data | childId |
---|---|---|
0 | A | 1,2 |
1 | B | 3 |
2 | C | 4,5 |
3 | D | 6,7,8 |
4 | E | 9 |
5 | F | Null |
6 | G | Null |
7 | H | Null |
8 | I | Null |
9 | J | Null |
优点:1.已知节点查找其孩子节点,比较容易直接通过节点孩子节点地址即可查找到,若是查找某个孩子,遍历集合(底层为单链表或数组),时间复杂度为O[1].
例如:已知D(5)节点 ,查找其孩子点,可直接通过F节点中孩子地址(id:6,id:7,id:8),直接查找。
缺点:1.已知节点查找其父节点,需要遍历整个数据结构查找,如D
父级节点:select * from table where childId like ‘7,%’ or childId like ‘%,7,%’ or childId like ‘%,7’ 需要扫描整个数据行一一对比(该表childId未加索引的情况下).
2.节点的度没办法控制,孩子越多度的值越大。
注释:多唠叨一句这里既是加索引也无效,%,7,% 和 %,7 会使索引失效,or 必须都走索引 ,整个条件才会走索引
2.3.孩子兄弟表示发
结构代码定义:
@Data
public class TreeChildBrothersDemo {
//根节点
private Node root;
public TreeChildBrothersDemo(String data, Node firstChild, Node rightBrother) {
this.root = new Node(data,firstChild,rightBrother);
}
@Data
class Node{
public Node(String data, Node firstChild, Node rightBrother) {
this.data = data;
this.firstChild = firstChild;
this.rightBrother = rightBrother;
}
/**
* 数据域 存储A,B,C等
*/
private String data;
/**
* 第一个孩子
*/
private Node firstChild;
/**
* 该节点的右兄弟
*/
private Node rightBrother;
}
}
如图:
注释:这里必须用弄清楚,孩子为该节点第一个最左边孩子,兄弟为该节点的右兄弟。如:D 是B的第一个孩子,C 是B的右兄弟。
与上边两种表示方法不同,双亲和孩子表示法树的深度和度都为4和3,孩子兄弟表示法树的深度为6 而树的度为2 (二叉树)
也就是把树转换为二叉树存储结构来存储。
数据库存储:
id | data | firstchild | rightBrother |
---|---|---|---|
0 | A | 1 | Null |
1 | B | 3 | 2 |
2 | C | 4 | Null |
3 | D | 6 | Null |
4 | E | 9 | 5 |
5 | F | Null | Null |
6 | G | Null | 7 |
7 | H | Null | 8 |
8 | I | Null | Null |
9 | J | Null | Null |
优点:1.已知某个节点查找某个孩子节点,只需要通过firstchild 找到第一个孩子(最左边孩子),通过该孩子的rightBrother ,查找第二个孩子… 一直查找下去,直到查找到具体孩子。
2.该树的度是固定的为2 ,其实这个表示法最大的好处就是把一棵树变成了一棵二叉树。
缺点:1已知节点查找其父节点,需要遍历整个数据结构查找,如D
父级节点:select * from table where firstchild = 3 or rightBrother= 3 需要扫描整个数据行一一对比(该表firstchild ,rightBrother 未加索引的情况下).
注释:如果真的有必要,完全可以再增加一个parentId 指针域来解决快速查找的双亲的问题。
3.二叉树
3.1二叉树概念:二叉树是n(n>=0)个节点的有限集合,该集合或者为空集,或者有一个根节点和两棵互不相交的,分别称为根节点的左子树和右子树的二叉树组成。
注释:通俗的讲就是树的度为2的树,就是二叉树数。
3.2二叉树特点:
- 每棵树最多有两颗子树,也就是二叉树的度不能大于2。
- 左子树和右子树是有顺序的,次序不能任意颠倒。
- 即使树中有一棵子树,也要区分左子树还是右子树 。如下图,树1和树2 他们是同一棵树,但是它们是不同的二叉树。
3.3 特殊二叉树
1.斜树:所有节点都只有左子树的二叉树叫左斜树 如下图 树1。所有节点都是只有右子树的二叉树叫右斜树,如下图 树2这两者统称为斜树。
2.满二叉树:在一颗二叉树中,如果所有分支节点都存在左子树和右子树,并且所有叶子节点都在同一层上,这样的二叉树称为满二叉树
如下图就是一棵满二叉树
3.完全二叉树
完全二叉树:对一个棵具有n个节点的二叉树按层序编号,如果编号为i(1<=i >=n)的节点与同样深度的满二叉树中编号为i的节点在二叉树中位置完全相同,则这颗二叉树称为完全二叉树。
如下图 树1 为完全二叉树,树2 和树3 不能为完全二叉树。
4.二叉树的性质:
- 在二叉树的第i层上至多有2^(i-1) 个节点(i>=1)。
- 深度为k的二叉树至多有2^k -1 个节点。
- 度为0的节点数N0 ,度为2的节点数N2 关系为N0=N2+1。
- 具有n个节点的完全二叉树的深度为|Log2^n|+1
3.4 二叉树的存储
1.结构代码定义:
@Data
public class TwoThree {
private Node root;
public TwoThree(Integer data) {
this.root = new Node(data);
}
class Node{
public Node(Integer data) {
this.data = data;
this.leftChild = null;
this.rightChild = null;
}
/**
* 数据域
*/
Integer data;
/**
* 左孩子地址
*/
private Node leftChild;
/**
* 右孩子地址
*/
private Node rightChild;
}
}
如图
数据库存储(这里用数据库表示不太准确):
id | data | leftchild | rightchild |
---|---|---|---|
0 | A | 1 | 2 |
1 | B | 3 | 4 |
2 | C | 5 | 6 |
3 | D | 7 | 8 |
4 | E | 9 | Null |
5 | F | Null | Null |
6 | G | Null | Null |
7 | H | Null | Null |
8 | I | Null | Null |
9 | J | Null | Null |
2.二叉树的遍历:
1.先序遍历
/**
* 先序遍历(第一次访问节点的时候打印节点)
* @param node
*/
public void preOrderX(Node node) {
if(node == null) {
return ;
}
System.out.println(node.data);
preOrderX(node.leftChild);
preOrderX(node.rightChild);
}
结果:ABDHIEJACFG
2.中序遍历
/**
* 中序遍历(第二次访问节点的时候打印节点)
* @param node
*/
public void preOrderX(Node node) {
if(node == null) {
return ;
}
preOrderX(node.leftChild);
System.out.println(node.data);
preOrderX(node.rightChild);
}
结果:HDIBJEAFCG
2.后序遍历
/**
* 中序遍历(第三次访问节点的时候打印节点)
* @param node
*/
public void preOrderX(Node node) {
if(node == null) {
return ;
}
preOrderX(node.leftChild);
preOrderX(node.rightChild);
System.out.println(node.data);
}
结果:HIJEBFGCA