父节点表示法的思想是让每个节点持有它的父节点的索引,这种方式是从子节点出发;反过来可以让父节点持有它的所有子节点。这种方式下,由于每个父节点需要记住多个子节点,因此必须采用“子节点链”表示法。如下图:
Java实现代码
package com.liuhao.DataStructures;
import java.util.ArrayList;
import java.util.List;
public class TreeChild<E> {
private static class ChildNode {
private int pos;// 记录当前节点位置
private ChildNode next;
public ChildNode(int pos, ChildNode next) {
this.pos = pos;
this.next = next;
}
}
public static class Node<T> {
T data;
ChildNode first;// 记录第一个子节点
public Node() {
}
public Node(T data) {
this.data = data;
this.first = null;
}
public String toString() {
if (first != null) {
return "TreeChild$Node[data=" + data + ", first=" + first.pos
+ "]";
}
return "TreeChild$Node[data=" + data + ", first=-1]";
}
}
private final int DEFAULT_TREE_SIZE = 100;
private int treeSize = 0;
// 使用一个Node[]数组来记录该树的所有节点
private Node<E>[] nodes;
// 记录节点数
private int nodeNums;
// 以指定根节点创建树
@SuppressWarnings("unchecked")
public TreeChild(E data) {
treeSize = DEFAULT_TREE_SIZE;
nodes = new Node[treeSize];
nodes[0] = new Node<E>(data);
nodeNums++;
}
// 以指定根节点、指定treeSize创建树
@SuppressWarnings("unchecked")
public TreeChild(E data, int treeSize) {
this.treeSize = treeSize;
this.nodes = new Node[treeSize];
nodes[0] = new Node<E>(data);
nodeNums++;
}
// 为指定节点添加子节点
public void addNode(E data, Node<E> node) {
for (int i = 0; i < treeSize; i++) {
if (nodes[i] == null) {
nodes[i] = new Node<E>(data);
// 若该节点没有第一个子节点,那么就新建子节点链
if (node.first == null) {
node.first = new ChildNode(i, null);
} else {
// 若有,则依次取该节点的子节点,直到叶子节点
ChildNode next = node.first;
while (next.next != null) {
next = next.next;
}
// 在叶子节点处添加该子节点
next.next = new ChildNode(i, null);
}
nodeNums++;
return;
}
}
throw new RuntimeException("该树已满,无法添加新节点");
}
// 判断是否为空
public boolean isEmpty() {
// 跟节点是否为空
return nodes[0] == null;
}
// 获取根节点
public Node<E> getRoot() {
return nodes[0];
}
// 获取指定节点的所有子节点
public List<Node<E>> getChildren(Node<E> node) {
List<Node<E>> list = new ArrayList<Node<E>>();
// 获取给定节点的第一子节点
ChildNode next = node.first;
// 沿着孩子链不断搜索下一个孩子节点
while (next != null) {
// 添加孩子链中的节点
list.add(nodes[next.pos]);
next = next.next;
}
return list;
}
// 返回指定节点的第index个子节点
public Node<E> getChildByIndex(Node<E> node, int index) {
// 获取该节点的第一个子节点
ChildNode next = node.first;
// 沿着孩子链一直搜寻
for (int i = 0; next != null; i++) {
if (index == i) {
return nodes[next.pos];
}
next = next.next;
}
return null;
}
// 递归的方式返回某个节点的深度
private int getDeep(Node<E> node) {
if (node.first == null) {
return 1;
} else {
int max = 0;
ChildNode next = node.first;
while (next != null) {
// 获取以其子节点为根的子树的深度
int tmp = this.getDeep(nodes[next.pos]);
if (tmp > max) {
max = tmp;
}
next = next.next;
}
return max + 1;
}
}
public int getDeep() {
return this.getDeep(getRoot());
}
// 返回包含指定节点的索引
public int pos(Node<E> node) {
for (int i = 0; i < treeSize; i++) {
if (nodes[i] == node) {
return i;
}
}
return -1;
}
}
从以上程序可以看出,定义树节点时增加了一个first域。该first域用于保存对该节点的子节点链的引用,通过这种方式即可记录数中节点之间的父子关系。
添加节点时只需找到指定节点的子节点链的最后节点,并让他指向新增的节点。
测试代码:
package com.liuhao.test;
import java.util.List;
import org.junit.Test;
import com.liuhao.DataStructures.TreeChild;
import com.liuhao.DataStructures.TreeChild.Node;
public class TreeChildTest {
@Test
public void test() {
TreeChild<String> tree = new TreeChild<String>("root");
Node<String> root = tree.getRoot();
System.out.println("根节点:" + root);
tree.addNode("节点1", root);
tree.addNode("节点2", root);
tree.addNode("节点3", root);
System.out.println("添加子节点后的根节点:" + root);
System.out.println("树的深度:" + tree.getDeep());
List<Node<String>> nodes = tree.getChildren(root);
System.out.println("根节点的第一个子节点:" + nodes.get(0));
tree.addNode("节点4", nodes.get(0));
System.out.println("根节点的第一个子节点:" + nodes.get(0));
System.out.println("树的深度:" + tree.getDeep());
}
}
显然,子节点链表示法中的每个节点都可以快速找到它的所有子节点,但相应的找父节点则比较麻烦。
您的关注是我坚持写作的动力,如果觉得有用,欢迎关注我的微信,海量学习资源免费送!