爲什麼要使用樹?
因爲它通常結合了另外兩種數據結構的優點:一種是有序數組,另一種是鏈表;大家都知道,在數組中插入數據太慢,而在鏈表中查找數據也是太慢,要是有一種數據結構,既能像鏈表那樣快速的插入和刪除,又能像數組那樣快速查找,那樣就好了;而這時,樹就出現了;
樹的有關定義:
(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內部類實現樹