为什么要使用树?
因为它通常结合了另外两种数据结构的优点:一种是有序数组,另一种是链表;大家都知道,在数组中插入数据太慢,而在链表中查找数据也是太慢,要是有一种数据结构,既能像链表那样快速的插入和删除,又能像数组那样快速查找,那样就好了;而这时,树就出现了;
树的有关定义:
(1)度(Degree):结点拥有的子树称为结点的度;度为0的结点又称为叶结点(Leaf)或终端结点;度不为0的结点称为非终端结点或分支结点;而树的度是树内各结点的度的最大值;
(2)深度(Depth)或高度:树中结点的最大层次称为树的深度或高度;
(3)如果将树中结点的各子树看成从左到右是有次序的,不能互换的,则该树称为有序树,否则称为无序树;
同样树的实现我们也可以参考线性表的顺序存储和链式存储两种方式:
注:同样可以使用内部类来实现;
链式存储相关代码如下:
package linkedTree;
//定义一个存储结点信息的结点类
public class Node<T>
{
private T data; //结点信息
private SubNode son; //子结点引用
public Node(){}
public Node(T data)
{
this.data = data;
this.son = null;
}
public Node(T data, SubNode son)
{
this.data = data;
this.son = son;
}
public void setData(T data)
{
this.data = data;
}
public T getData()
{
return this.data;
}
public void setSon(SubNode son)
{
this.son = son;
}
public SubNode getSon()
{
return this.son;
}
@Override
public String toString()
{
return "节点:" + this.data;
}
}
package linkedTree;
//定义一个用于存储子结点位置的结点类
public class SubNode
{
private int location;
private SubNode next;
public SubNode()
{
this.location = 0;
this.next = null;
}
public SubNode(int location)
{
this.location = location;
this.next = null;
}
public SubNode(int location, SubNode next)
{
this.location = location;
this.next = next;
}
public void setLocation(int location)
{
this.location = location;
}
public int getLocation()
{
return this.location;
}
public void setNext(SubNode next)
{
this.next = next;
}
public SubNode getNext()
{
return this.next;
}
}
package linkedTree;
import java.util.LinkedList;
import java.util.List;
//定义链式存储的树类,采用递归求解树的深度
public class MyLinkTree<T>
{
private final int DEFAUL_SIZE = 10;
private int size;
private int count;
private Node<T>[] nodes;
@SuppressWarnings("unchecked")
public MyLinkTree()
{
this.size = this.DEFAUL_SIZE;
this.nodes = new Node[this.size];
this.count = 0;
}
@SuppressWarnings("unchecked")
public MyLinkTree(int size)
{
this.size = size;
this.nodes = new Node[this.size];
this.count = 0;
}
public MyLinkTree(T data)
{
this();
Node<T> node = new Node<T>(data);
this.nodes[0] = node;
this.count++;
}
public MyLinkTree(Node<T> root)
{
this();
this.nodes[0] = root;
this.count++;
}
public void add(Node<T> node, Node<T> parent)
{
SubNode son = new SubNode();
for (int i = 0; i < this.size; i++)
{
if (this.nodes[i] == null)
{
this.nodes[i] = node;
son.setLocation(i);
break;
}
}
// 往链表中添加子节点位置
SubNode next = parent.getSon();
if (next != null)
{
while (next.getNext() != null)
{
next = next.getNext();
}
next.setNext(son);
}
else
{
parent.setSon(son);
}
this.count++;
}
public int size()
{
return this.count;
}
//返回根节点数据
public Node<T> getRoot()
{
return this.nodes[0];
}
// 获取指定节点的子节点
public List<Node<T>> getSon(Node<T> parent)
{
List<Node<T>> list = new LinkedList<Node<T>>();
SubNode son = parent.getSon();
while (son != null)
{
list.add(this.nodes[son.getLocation()]);
son = son.getNext();
}
return list;
}
// 获取树的深度,通过递归的方式来解决
public int deep()
{
/*
int max = 0;
for (int i = 0; i < this.count; i++)
{
int temp = this.deep(this.nodes[i]);
max = max > temp ? max : temp;
}
return max;
*/
return deep(getRoot());
}
public int deep(Node<T> node)
{
SubNode son = node.getSon();
if (son == null)
{
return 1;
}
else
{
int max = 0;
while (son != null)
{
int temp = this.deep(this.nodes[son.getLocation()]);
max = temp > max ? temp : max;
son = son.getNext();
}
//最后返回其所有子树的最大深度 + 1
return max + 1;
}
}
}
package linkedTree;
public class Client
{
public static void main(String[] args)
{
Node<String> root = new Node<String>("A");
Node<String> b = new Node<String>("B");
Node<String> c = new Node<String>("C");
Node<String> d = new Node<String>("D");
Node<String> e = new Node<String>("E");
Node<String> f = new Node<String>("F");
Node<String> g = new Node<String>("G");
Node<String> h = new Node<String>("H");
MyLinkTree<String> tree = new MyLinkTree<String>(root);
tree.add(b, root);
tree.add(c, root);
tree.add(d, root);
tree.add(e, b);
tree.add(f, b);
tree.add(g, f);
tree.add(h, g);
System.out.println(tree.size());
System.out.println(tree.deep());
System.out.println(tree.getSon(b));
System.out.println(tree.getRoot());
}
}
而树的顺序存储结构为:data是数据域,存储结点的数据信息,而parent是指针域,存储结点的位置信息;
下标 | data | parent |
0 | A | -1 |
1 | B | 0 |
2 | C | 0 |
3 | D | 0 |
4 | E | 1 |
5 | F | 1 |
6 | G | 2 |
7 | H | 3 |
代码如下:
package arrayTree;
//结点信息
public class Node<T>
{
private T data;
private int parent;
public Node()
{
}
public Node(T data)
{
this.data = data;
}
public Node(T data, int parent)
{
this.data = data;
this.parent = parent;
}
public void setData(T data)
{
this.data = data;
}
public T getData()
{
return this.data;
}
public void setParent(int parent)
{
this.parent = parent;
}
public int getParent()
{
return this.parent;
}
public String toString()
{
return "节点:" + this.data;
}
}
package arrayTree;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MyTree<T>
{
private final int DEFAULT_SIZE = 10; //数组初始值为10,用完时扩充;
private int size; //数组元素
private int count; //记录结点数
private Object[] nodes; //使用一个数组来记录该树里的所有节点
public MyTree()
{
this.size = this.DEFAULT_SIZE;
this.nodes = new Object[this.size];
this.count = 0;
}
//以指定根结点创建树
public MyTree(Node<T> root)
{
this();
this.count = 1;
this.nodes[0] = root;
}
//指定根结点,指定数组的长度来创建树
public MyTree(Node<T> root, int size)
{
this.size = size;
this.nodes = new Object[this.size];
this.count = 1;
this.nodes[0] = root;
}
// 添加一个节点
public void add(Node<T> node)
{
for (int i = 0; i < this.size; i++)
{
if (this.nodes[i] == null)
{
nodes[i] = node;
break;
}
}
this.count++;
}
//检查数组是否用完,用完调用enlarge来扩充;
public void check()
{
if (this.count >= this.size)
{
this.enlarge();
}
}
// 添加一个节点,并指明父节点
public void add(Node<T> node, Node<T> parent)
{
this.check();
node.setParent(this.position(parent));
this.add(node);
}
// 获取节点在数组的存储位置
public int position(Node<T> node)
{
for (int i = 0; i < this.size; i++)
{
if (nodes[i] == node)
{
return i;
}
}
return -1;
}
// 获取整棵树有多少结点
public int getSize()
{
return this.count;
}
public int getCount()
{
return this.size;
}
// 获取根节点
@SuppressWarnings("unchecked")
public Node<T> getRoot()
{
return (Node<T>) this.nodes[0];
}
// 获取所以节点,以List形式返回
@SuppressWarnings("unchecked")
public List<Node<T>> getAllNodes()
{
List<Node<T>> list = new ArrayList<Node<T>>();
for (int i = 0; i < this.size; i++)
{
if (this.nodes[i] != null)
{
list.add((Node<T>) nodes[i]);
}
}
return list;
}
// 获取树的深度,只有根节点时为1
@SuppressWarnings("unchecked")
public int getDepth()
{
int max = 1;
if (this.nodes[0] == null)
{
return 0;
}
for (int i = 0; i < this.count; i++)
{
int deep = 1;
int location = ((Node<T>) (this.nodes[i])).getParent();
while (location != -1 && this.nodes[location] != null)
{
location = ((Node<T>) (this.nodes[location])).getParent();
deep++;
}
if (max < deep)
{
max = deep;
}
}
return max;
}
//扩充数组
public void enlarge()
{
this.size = this.size + this.DEFAULT_SIZE;
Object[] newNodes = new Object[this.size];
newNodes = Arrays.copyOf(nodes, this.size);
Arrays.fill(nodes, null);
this.nodes = newNodes;
System.out.println("扩充数组");
}
}
测试信息:
package arrayTree;
public class Client
{
public static void main(String[] args)
{
Node<String> root = new Node<String>("A", -1);
Node<String> b = new Node<String>("B");
Node<String> c = new Node<String>("C");
Node<String> d = new Node<String>("D");
Node<String> e = new Node<String>("E");
Node<String> f = new Node<String>("F");
Node<String> g = new Node<String>("G");
MyTree<String> tree = new MyTree<String>(root);
tree.add(b, root);
tree.add(c, root);
tree.add(d, root);
tree.add(e, b);
tree.add(f, b);
tree.add(g, f);
System.out.println(tree.getSize());
System.out.println(tree.getRoot());
System.out.println(tree.getAllNodes());
System.out.println(tree.getDepth());
tree.add(new Node<String>("H"), g);
System.out.println(tree.getDepth());
}
}
至于内部类的实现方式可以参考: Java内部类实现树