树的子节点链表示法及其Java实现

父节点表示法的思想是让每个节点持有它的父节点的索引,这种方式是从子节点出发;反过来可以让父节点持有它的所有子节点。这种方式下,由于每个父节点需要记住多个子节点,因此必须采用“子节点链”表示法。如下图:

 

树的子节点链表示法及其Java实现_第1张图片树的子节点链表示法及其Java实现_第2张图片


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());

	}

}


显然,子节点链表示法中的每个节点都可以快速找到它的所有子节点,但相应的找父节点则比较麻烦。





你可能感兴趣的:(java,树)