Java基础 - 二叉树的二叉链表存储

package com.yc.tree;


/**
 * 
 * @author wb
 * @param <E>
 * 
 * _____________________
 * │    │	 │     
 * │ ● │ data │ ● │
 * │____│______│____│
 *    │	    │
 *    ↓left	    ↓right
 * 
 * 二叉链表存储的思想是让每个节点都能“记住”它的左、右两个子节点。为每个节点增加left、right两个指针,分别引用该节点的左、右两个子节点。
 * 因此二叉链表存储的每个节点有如下图所示的结构:
 * 
 * 二叉链表存储的二叉树节点有如下定义
 * class Node{
 * 	Object data;
 * 	Node left;
 * 	Node right;
 * }
 * 对于这种二叉链表存储的二叉树,如果程序需要,为指定节点添加子节点也非常容易,让父节点的left或right引用指向新节点即可。
 * 
 * 下面程序加粗的字体代码就是二叉链表存储的关键,二叉树中每个节点保留left、right两个引用,分别指向该节点的左右两个子节点。
 * 通过这种方式即可建立二叉树各个节点之间的父子关系。
 * 
 * 下面是程序实现:
 */
public class TwoLinkBinTree <E> {
	public static class Node{
		Object data;
		Node left;
		Node right;
		
		public Node(){
		}
		public Node(Object data){
			this.data = data;
		}
		public Node(Object data, Node left, Node right){
			this.data = data;
			this.left = left;
			this.right = right;
		}
	}
	
	private Node root;
	
	public TwoLinkBinTree(){
		this.root = new Node();
	}
	public TwoLinkBinTree(Object data){
		this.root = new Node(data);
	}
	
	
	
	//根据根节点中的data判断是否为空树
	public boolean isEmpty(){
		return root.data == null;
	}
	
	//返回根节点
	public Node root(){
		exceptionForRoot();
		return root;
	}
	/**
	 * 为指定节点添加子节点
	 * @param parent :指定节点
	 * @param data	   :数据元素
	 * @param isLeft :是否是左子节点
	 */
	public void add(Node parent, Object data, boolean isLeft){
		exceptionForAdd(parent, isLeft);
		Node newNode = new Node(data);
		if(isLeft){
			parent.left = newNode;
		}else{
			parent.right = newNode;
		}
	}
	
	/*
	 * 向指定节点添加子节点并返回该子节点
	 */
	public Node addAndReturn(Node parent, Object data, boolean isLeft){
		exceptionForAdd(parent, isLeft);
		Node newNode = new Node(data);
		if(isLeft){
			parent.left = newNode;
		}else{
			parent.right = newNode;
		}
		return newNode;
	}
	
	//返回非根节点的父节点
	public Node parent(Node node){
		exceptionForParent(node);
		//采用二叉链表存储的树要返回某节点的父节点需遍历整个二叉树,但由于它是链式的,所以遍历起来非常不方便。
		//对于链式存储的树的遍历在后面会讲。
		return null;
	}
	
	//返回左子节点
	@SuppressWarnings("unchecked")
	public E leftChild(Node node){
		exceptionForLeftChild(node);
		
		return  node.left == null ? null : (E)node.left.data;
	}
	
	//返回右子节点
	@SuppressWarnings("unchecked")
	public E rightChild(Node node){
		exceptionForRightChild(node);
			
		return  node.right == null ? null : (E)node.right.data;
	}
	
	//返回该树的深度
	public int deep(){
		return deep(root);
	}
	
	private int deep(Node node) {
		if(node == null){
			return 0;
		}
		
		if(node.left == null && node.right == null){
			return 1;
		}else{
			int deepRight = deep(node.right);
			int deepLeft = deep(node.left);
			return deepLeft > deepRight ? deepLeft + 1 : deepRight + 1;
		}
	
	}
	private void exceptionForAdd(Node parent, boolean isLeft){
		if(parent == null){
			throw new RuntimeException("指定节点在二叉链表中不存在,无法为其添加子节点");
		}
		if(isLeft && parent.left != null){
			throw new RuntimeException("指定节点在二叉链表中已存在左子节点");
		}
		if(!isLeft && parent.right != null){
			throw new RuntimeException("指定节点在二叉链表中已存在右子节点");
		}
	}
	private void exceptionForRoot(){
		if(isEmpty()){
			throw new RuntimeException("该树为空树,无根节点");
		}
	}
	private void exceptionForParent(Node node){
		if(node == root){
			throw new RuntimeException("该节点为根节点,无父节点");
		}
		if(node == null){
			throw new RuntimeException("该节点为null,无父节点");
		}
	}
	private void exceptionForLeftChild(Node node){
		if(node == null){
			throw new RuntimeException("该节点为null,无左子节点");
		}
	}
	private void exceptionForRightChild(Node node) {
		if(node == null){
			throw new RuntimeException("该节点为null,无右子节点");
		}
	}
}


测试代码如下:

/**
 * 对于这种二叉链表的二叉树,因为程序采用链表来记录树中所有节点,所以添加节点没有限制,而且不会像顺序存储那样产生大量的空间浪费。
 * 当然,这种二叉链表的存储方式在遍历树节点时的效率不高,指定节点访问其父节点是也是比较苦难,程序必须采用遍历二叉树的方式来搜索其父节点。
 * 为了克服二叉链表存储方式中访问父节点不方便的问题,可以将二叉链表扩展成三叉链表。
 * 
 * **/
public class TwoLinkBinTreeTest {
	@SuppressWarnings("unused")
	public static void main(String[] args) {
		TwoLinkBinTree<String> tree = new TwoLinkBinTree<String>("根节点");
		//依次添加节点
		Node n2_l = tree.addAndReturn(tree.root(), "第二层左节点", true);
		Node n2_r = tree.addAndReturn(tree.root(), "第二层右节点", false);
		Node n3_l = tree.addAndReturn(n2_r, "第三层左节点", true);
		Node n3_r = tree.addAndReturn(n2_r, "第三层右节点", false);
		Node n4_r = tree.addAndReturn(n3_r, "第四层右节点", false);
		
		System.out.println("n2_r的左子节点为:" + tree.leftChild(n2_r));
		System.out.println("n2_r的右子节点为:" + tree.rightChild(n2_r));
		System.out.println( "该树此时的深度为:" + tree.deep());
	}
}

测试结果如下:

n2_r的左子节点为:第三层左节点
n2_r的右子节点为:第三层右节点
该树此时的深度为:4


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值