树的笔记
一:树的定义
树是由一个集合以及在该集合上定义的一种关系构成的。
二:一些基本术语
1:树:指的是N个父子关系的结点的有限集合。
空树:集合为空,即N=0。
2:树的度:树中所有节点的度的最大值就是该树的度。
3:树的深度:树中节点的最大值称为的深度或高度。
4:树的节点:包含一个数据元素和若干指向其子树的分支。
(1):根节点:在任意非空树中,有且只有一个根节点。
(2):叶子节点(终端结点):度为0的结点。
(3):分支结点(非终端结点,普通节点):度不为0的结点
——按是否包含子节点来分,节点分为两种
————普通节点:包含子节点的节点。
————叶子结点:没有子节点的节点,因此叶子结点不可作为父节点。
——按节点是否具有唯一的父节点来分:
————根节点:没有父节点的节点,根节点不可作为父节点。
————普通节点:具有唯一父节点的节点。
5:节点的度(degree):结点拥有的子树的数目。
6:子节点,父节点,兄弟节点:
节点的子树的个被称为该结点的子节点,
而该节点称做子节点的父节点。
具有相同父节点的子节点互称为兄弟节点。
7:节点的层次:节点的层次从根开始算起,根的层次值为1,其余节点的层次值为父节点层次加1.
8:祖先:结点的祖先是从根到该结点所经分支上所有的结点。
9:子孙:以某结点为根的子树中的任意一结点都称为该结点的子孙。
10 :有序树与无序树如果将树中节点的各个子树看成从左到右是有序的,则称为有序树,否则称为无序树。
11:森林:森林是2颗或2颗以上互不相交的数的集合,删去一个树的根,就得到了一个森林。
三:树的基本操作:
1:初始化:通常是一个构造器,用于创建一颗空的树,或者以指定节点为根来创建树。
2:为根节点添加子节点。
3:判断树是否为空。
4:返回根节点。
5:返回指定节点(非根节点)的父节点
6:返回指定节点(非叶子结点)的所有子节点
7:返回指定节点(非叶子结点)的第i个字节点
8:返回该树的深度。
9:返回指定节点的位置。
三:树的存储结构:
父节点表示法:每个子节点都记录它的父节点。(使用数组顺序存储)
子节点链表示法,每个非叶子结点通过一个链表来记录它所有的子节点。(链式存储)
四:父节点表示法
1:简单介绍:
可知树中除根节点之外的每个节点都有一个父节点,为了记录节点与节点之间的父子关系,
可以为每个节点增加一个parent域,用以记录该节点的父节点。
用一个节点数组来保存树里的每个节点,并让每个节点记录其父节点的在数组索引即可。
因此父节点表示法的思想:就是让每个节点记住它的父节点的索引,这种方式是从子节点着手。
2:范例树
3:记录范例树
4:代码实现:java代码实现,采用父节点表示法实现一颗树。
package kejian;
import javax.swing.plaf.basic.BasicTreeUI;
import java.util.ArrayList;
import java.util.List;
public class TreeParent<E> {
public static class Node<T> {
T data;
int parent;//增加的parent域,用来记录节点的父节点。
//下面三个方法进行节点的初始化
public Node() {
}
public Node(T data) {
this.data = data;
}
public Node(T data, int parent) {
this.data = data;
this.parent = parent;
}
public String toString() {
return "TreeParent$Node[date" + data + ",parent="
+ parent + "]";
}
}
private final int default_tree_size=100;
private int treeSize=0;
private Node<E>[] nodes;//记录该树里的所有节点
private int nodeNums;//记录节点数
public TreeParent(E data)
{//以指定的根节点创建树
treeSize=default_tree_size;
nodes=new Node[treeSize];
nodes[0]=new Node<E>(data,-1);
nodeNums++;
}
public TreeParent(E data,int treeSize)
{//以指定根节点,指定treeSize创建树。
this.treeSize=treeSize;
nodes=new Node[treeSize];
nodes[0]=new Node<E>(data,-1);
nodeNums++;
}
public void addNode(E data,Node parent)
{//为指定节点添加子节点
for(int i=0;i<treeSize;i++){
//找到数组中第一个为null的元素,该元素保存新节点
if(nodes[i]==null){
//创建新节点,并用指定的数组元素保存它
nodes[i]=new Node(data,pos(parent));
nodeNums++;
return ;
}
}
throw new RuntimeException("该树已满,无法添加新节点");
}
public boolean empty()
{//判断树是否为空
return nodes[0]==null;
}
public Node<E> root()
{//返回根节点
return nodes[0];
}
public Node<E> parent(Node node)
{//返回节点的父节点
return nodes[node.parent];
//每个节点的parent记录的节点的位置
}
public List<Node<E>> children(Node parent)
{//返回指定节点的所有节点
List<Node<E>> list=new ArrayList<Node<E>>();
for(int i=0;i<treeSize;i++){
//如果当前节点的父节点的位置等于parent节点的位置
if(nodes[i]!=null && nodes[i].parent==pos(parent))
{
list.add(nodes[i]);
}
}
return list;
}
public int deep()
{//返回该树的深度
int max=0;
for(int i=0;i<treeSize && nodes[i]!=null;
i++)
{
int def=1;//初始化本节点的深度
int m=nodes[i].parent;//m记录当前节点的父节点的位置
while(m!=-1 && nodes[m]!=null){//如果父节点存在
//向上继续搜索父节点
m=nodes[m].parent;
def++;
}
if(max<def)
max=def;
}
return max;
}
public int pos(Node node)
{//返回包含指定值的节点
for(int i=0;i<treeSize;i++)
{
if(nodes[i]==node)
return i;
}
return -1;
}
public static void main(String[] args){
TreeParent<String> t=new TreeParent<String>("root");
TreeParent.Node root=t.root();
System.out.println(root);
t.addNode("节点1",root);
System.out.println("此树的深度:"+t.deep());
t.addNode("节点2",root);
//获取根节点的所有子节点
List<TreeParent.Node<String>> nodes=t.children(root);
System.out.println("根节点的第一个节点:"+nodes.get(0));
//为根节点的第一个子节点新增一个子节点
t.addNode("节点3",nodes.get(0));
System.out.println("此树的深度:"+t.deep());
}
}
5:父节点表示法的特点是:
每个节点都可以快速找到它的父节点。但是如果要找某个节点的所有子节点就比较麻烦,程序要遍历整个节点数组。
五:子节点链表示法:
1:简单介绍
子节点链表示法:让父节点记住它的所有子节点,在这种方式下,由于每个父节点需要记住多个子节点,
因此必须采用“子节点链”表示法。采用子节点表示法来记录树时,需要为每个节点维护一个子节点链。
通过该子节点链来记录该节点的所有子节点。
2:范例树
3:子节点链表示法:A后面的节点链为A节点的所有子节点,同理B,C,D,E……
4:代码:java代码实现子节点链表示法。
定义树节点的first域:用于保存对该节点的子节点链的引用,通过这种方式记录树中节点之间的父子关系。
使用这种子节点链表示法来存储树时,添加节点时只需找到指定父节点的子节点链
的最后节点,并让它指向新增的节点。
package kejian;
import java.util.ArrayList;
import java.util.List;
/**
* Created by 20378 on 2018-11-02.
*/
public class TreeChild<E> {
private static class SonNode {
private int pos;//记录当前节点位置
private SonNode next;
public SonNode(int pos, SonNode next) {
this.pos = pos;
this.next = next;
}
}
public static class Node<T> {
T data;
SonNode first;//记录第一个节点
public Node(T data) {
this.data = data;
this.first = null;
}
public String toString() {
if (first != null)
{
return "TreeChild $Node[data" +
data + ",first" + first.pos + "]";
} else
{
return "TreeChild $Node[data=" +
data + ",first=-1]";
}
}
}
private final int default_tree_size = 100;
private int treeSize = 0;
private Node<E>[] nodes;//使用数组来记录该树里的所有节点
private int nodeNums;//记录节点数
public TreeChild(E data) {//以指定根节点创建树
treeSize = default_tree_size;
nodes = new Node[treeSize];
nodes[0] = new Node<E>(data);
nodeNums++;
}
public TreeChild(E data, int treeSize) {//以指定根节点,指定treeSize
this.treeSize = treeSize;
nodes = new Node[treeSize];
nodes[0] = new Node<E>(data);
nodeNums++;
}
public void addNode(E data, Node parent) {
for (int i = 0; i < treeSize; i++) {
//找到数组中第一个为null的元素,该元素保存新节点
if (nodes[i] == null) {
//创建新节点,并用指定数组元素来保存它
nodes[i] = new Node(data);
if (parent.first == null) {
parent.first = new SonNode(i, null);
} else {
SonNode next = parent.first;
while (next.next != null) {
next = next.next;
}
next.next = new SonNode(i, null);
}
nodeNums++;
return;
}
}
throw new RuntimeException("该树已满,无法添加新节点");
}
public boolean empty()
{
return nodes[0] == null;
}
public Node<E> root()
{
//返回根节点
return nodes[0];
}
public List<Node<E>> children(Node parent)
{
List<Node<E>> list = new ArrayList<Node<E>>();
SonNode next = parent.first;//获取parent节点的第一个节点
while (next != null)//沿着孩子链不断搜索下一个子节点
{
//添加新节点
list.add(nodes[next.pos]);
next = next.next;
}
return list;
}
public Node<E> child(Node parent,int index)
{//返回指定节点的第index个子节点
//获取parent节点的第一个子节点
SonNode next=parent.first;
//沿着孩子链不断搜索下一个节点
for(int i=0;next!=null;i++){
if(index==i)
return nodes[next.pos];
next=next.next;
}
return null;
}
public int deep()
{//返回该树的深度
return deep(root());
}
private int deep(Node node)
{
if(node.first==null)
return 1;
else
{
int max=0;
SonNode next=node.first;
//沿着孩子链不断搜索下一个节点
while(next!=null)
{
int tmp=deep(nodes[next.pos]);
if(tmp>max)
max=tmp;
next=next.next;
}
return max+1;//返回其所有子树的最大深度加1.
}
}
public int pos(Node node)
{//返回包含指定值的节点
for(int i=0;i<treeSize;i++)
{
//找到指定节点
if(nodes[i]==node)
return i;
}
return -1;
}
public static void main(String[] args)
{
TreeChild<String> t=new TreeChild<String>("root");
TreeChild.Node root=t.root();
System.out.println("根节点:"+root);
t.addNode("节点一",root);
t.addNode("节点二",root);
System.out.println("添加子节点后的根节点:"+root);
}
}
5:子节点链表示法的特点:
每个节点都可以快速找到它的所有子节点。但如果要找到某个父节点则比较麻烦,程序要遍历整个节点数组。