Java基础 - 树的实现(一)父节点表示法

父节点表示法:
通过前面的介绍可以发现,树中除根节点之外的每个节点都有一个父节点。为了记录树中节点与节点之间的父子关系,可以为每个节点增加一个parent域,用来记录该节点的父节点。对于如下图所示的数,可以用一个表(数组)来保存它。

下表记录示范树

数组索引dataparent
0A-1
1B0
2C0
3D0
4E1
5F3
6G3
7H4
8I4
9J4
10K6
.........


由此可见,只要用一个数组节点来保存树里的每个节点,并让每个节点都记录它的父节点在数组中的索引位置即可。下面的程序实现了父节点表示法的树:

import java.util.ArrayList;
import java.util.List;

/**
 * @see 父节点表示法
 * @author wb
 *
 * @param <E>
 */
public class TreeParent <E> {
	public class Node <T>{
		T data;
		int parent;
		
		Node(){
		}
		Node(T data){
			this.data = data;
		}
		Node(T data, int parent){
			this(data);
			this.parent = parent;
		}
		public String toString(){
			return "TreeParent$Node[data = " + data + ", parent = " + parent + "]";
		}
	}
	
	private final int DEFAULT_TREE_SIZE = 100;
	private int treeSize = 0; //相当于容量吧
	//用来记录树中所有的节点
	private Node<E>[] nodes;
	//用来记录树中节点的个数
	private int nodeNums;
	
	/**
	 * 三种构造方法
	 */
	@SuppressWarnings("unchecked")
	public TreeParent(){
		treeSize = DEFAULT_TREE_SIZE;
		nodes = new Node[treeSize];
	}
	public TreeParent(E element){
		this();
		nodes[0] = new Node<E>(element, -1);
		nodeNums ++;
	}
	@SuppressWarnings("unchecked")
	public TreeParent(E element, int initSize){
		treeSize = initSize;
		nodes = new Node[treeSize];
		nodes[0] = new Node<E>(element, -1);
		nodeNums ++;
	}
	
	
	//判断树是否为空
	public boolean isEmpty(){
		return nodes[0] == null;
	}
	//返回根节点
	public Node<E> root(){
		return nodes[0];
	}
	
	/**
	 * 为指定节点添加子节点
	 * @param element : 数据元素
	 * @param parent  : 某节点(要添加子节点的节点)
	 */
	public void addNode(E element, Node<?> parent){
		//找到nodes数组中第一个为null的位置并将新的节点添加到该位置
		for(int i = 0; i < treeSize; i ++){
			if(nodes[i] == null){
				Node<E> newNode = new Node<E>(element, pos(parent));
				nodes[i] = newNode;
				nodeNums ++;
				return;
			}
		}
		throw new RuntimeException("该树已满,无法添加新节点!");
	}
	
	/**
	 * 返回指定节点的父节点
	 * @param node	:	某指定节点
	 * @return		:	返回指定节点的父节点
	 */
	public Node<E> parent(Node<?> node){
		return nodes[node.parent];
	}

	/**
	 * 返回指定节点(非叶子节点)的所有子节点
	 * @param node	:指定节点(非叶子节点)
	 * @return		:所有子节点
	 */
	public List<Node<E>> childen(Node<?> node){
		List<Node<E>> list = new ArrayList<Node<E>>();
		int indexParen = pos(node);
		for(int i = 0; i < treeSize; i ++){
			if(nodes[i] != null && nodes[i].parent == indexParen){
				list.add(nodes[i]);
			}
		}
		return list;
	}
	
	/**
	 * @see :deep of this tree
	 * @return	:返回该树的深度
	 */
	public int deep(){
		int max = 0;
		for(int i = 0; i < treeSize && nodes[i] != null; i ++){
			//初始化本节点的层次
			int def = 1;
			//
			int pindex = nodes[i].parent;
			while(pindex != -1 && nodes[pindex] != null){
				//
				pindex = nodes[pindex].parent;
				def ++;
			}
			
			if(max < def){
				max = def;
			}
		}
		return max;
	}
	/**
	 * 根据指定节点返回在节点数组中的索引位置
	 * @param parent	:	指定节点
	 * @return			:	返回指定在节点数组中的索引位置
	 */
	public int pos(Node<?> parent) {
		for(int i = 0; i < treeSize; i ++){
			if(parent == nodes[i]){
				return i;
			}
		}
		return -1;
	}
}
从上面的程序可以看出,定义树节点时增加了一个parent域。该parent域用于保存该节点的父节点在数组中的位置索引,通过这种方法即可记录树中节点之间的父子关系。使用这种父节点表示法来存储树时,添加节点时将新节点的parent域的值设为其父节点在数组中的索引即可。下面是测试代码:

import java.util.List;

import com.yc.tree.TreeParent;

public class TreeParentTest {
	public static void main(String[] args) {
		TreeParent<String> tree = new TreeParent<String>("root");
		System.out.println("此时树的深度为: " + tree.deep());
		System.out.println();
		
		TreeParent<String>.Node<String> root = tree.root();
		tree.addNode("节点1", root);
		tree.addNode("节点2", root);
		System.out.println("此时树的深度为: " + tree.deep());
		System.out.println();
		
		List<TreeParent<String>.Node<String>> list = tree.childen(root);
		System.out.println( "根节点的第一个子节点为:" + list.get(0));
		//为根节点的第一个子节点添加子节点
		tree.addNode("节点3", list.get(0));
		System.out.println( "此时树的深度为: " + tree.deep());
	}
}

测试结果为:



通过上面的介绍可以发现,父节点表示法的特点是,每个节点都记录了它的父节点以至于可以快速的找到其父节点,但如果要去找每个节点的所有子节点就比较麻烦,程序要遍历整个节点数组(尽管遍历很快??)。