二叉树(一)---二叉树的三种创建方法

一、顺序存储
利用满二叉树的性质,每层的节点数1,2,4,8......,所以一颗深度为i的二叉树最多只能包含2 (i) - 1 (i是以指数的形式求出来的,此处利用等比数列求和),因此需要设置一个相同长度的数组来存储这个二叉树,若是空出来的节点,则数组元素留空即可。

参考代码:
public class ArrayBinaryTree {

	//使用数组来存储它的所有节点
	private Object[] datas;
	private int DEFAULT_DEEP = 10;
	
	//记录该树的深度
	private int deep;
	private int arraySize;
	
	//默认的深度创建一个二叉树
	public ArrayBinaryTree(){
		this.deep = DEFAULT_DEEP;
		this.arraySize = (int)Math.pow(2, deep) - 1;//等比数列求和
		datas = new Object[arraySize];
	}
	
	//以指定深度创建一个二叉树
	public ArrayBinaryTree(int deep){
		this.deep = deep;
		this.arraySize = (int)Math.pow(2, deep) - 1;
		datas = new Object[arraySize];
	}
	
	//以指定深度和指定的创建一个二叉树
	public ArrayBinaryTree(int deep, T root){
		this.deep = deep;
		this.arraySize = (int)Math.pow(2, deep) - 1;
		datas = new Object[arraySize];
		datas[0] = root;
	}
	
	/**
	 * 为指定的节点添加子节点
	 * @param index 需要添加的子节点的父节点的索引
	 * @param data 新的节点的数据
	 * @param left 是否为左节点
	 */
	public void addNode(int index, T data, boolean left){

		if(datas[index] == null){
			throw new RuntimeException("空节点,无法添加!");
		}
		if(2 * index + 1 >= arraySize){
			throw new RuntimeException("底层已满,越界异常!");
		}
		if(left){
			datas[2 * index + 1] = data;
		}
		else{
			datas[2 * index + 2] = data;
		}
	}
	
	//判断是否为空
	public boolean isEmpty(){
		return datas[0] == null;
	}

	public String toString(){
		return Arrays.toString(datas);
	}
}
测试代码和截图:
public class Test {
	
	public static void main(String[] args) {
		ArrayBinaryTree binTree = new ArrayBinaryTree(4, "root");
		binTree.addNode( 0, "two_right", false);
		binTree.addNode( 2, "three_right", false);
		binTree.addNode( 6, "four_right", false);
		System.out.println(binTree);
	}
}

二叉树(一)---二叉树的三种创建方法_第1张图片

由此可以看出浪费存储资源。只有四个元素,分配了15长度的数组。(截图不完整)

二、二叉链表存储
让每个节点都记住它的左右两个子节点,因而增加了left, right两个指针,分别指向他们的左节点和右节点。
参考代码:
public class TwoLinkBinaryTree {

	public static class TreeNode{
		Object data;
		TreeNode left;
		TreeNode right;
		public TreeNode(){
		}
		
		public TreeNode(Object data){
			this.data = data;
		}
		
		public TreeNode(Object data, TreeNode left, TreeNode right){
			this.data = data;
			this.left = left;
			this.right = right;

		}
	}
	private TreeNode root;

	//默认的构造器创建一个二叉树
	public TwoLinkBinaryTree(){
		this.root = new TreeNode();
	}

	//以指定的根元素创建一个二叉树
	public TwoLinkBinaryTree(E data){
		this.root = new TreeNode(data);
	}

	/**
	 * 为指定的节点添加子节点
	 * @param parent 父节点
	 * @param data 新的节点的数据
	 * @param left 是否为左节点
	 * @return 新增加的节点
	 */
	public TreeNode addNode(TreeNode parent, E data, boolean left){

		if(parent== null){
			throw new RuntimeException("空节点,无法添加!");
		}
		if(left && parent.left != null){
			throw new RuntimeException("已有左节点,无法添加!");
		}
		if(!left && parent.right != null){
			throw new RuntimeException("已有右节点,无法添加!");
		}
		TreeNode newNode = new TreeNode(data);
		if(left){
			parent.left = newNode;//指向这个对象 而不是指向这个值
		}
		else{
			parent.right = newNode;
		}
		return newNode;
	}

	//返回根节点
	public TreeNode root() {
		if(isEmpty()){
			throw new RuntimeException("空树!");
		}
		return root;
	}

	//判断是否为空
	private boolean isEmpty() {

		return root.data == null;
	}
	
	//返回指定节点的(非叶子)的左节点 无则返回null
	public E leftChild(TreeNode parent){
		if(parent == null){
			throw new RuntimeException("节点空,无法添加子节点!");
		}
		return parent.left == null ? null : (E)parent.left.data;
	}
	
	//返回指定节点的(非叶子节点)的右节点 无则返回null
	public E rightChild(TreeNode parent){
		if(parent == null){
			throw new RuntimeException("节点空,无法添加子节点!");
		}
		return parent.right == null ? null : (E)parent.right.data;
	}
}

测试部分及截图(增加了几个方法,让结果更有直观性):

代码:
public class Test {
	
	public static void main(String[] args) {
//		ArrayBinaryTree binTree = new ArrayBinaryTree(4, "root");
		TwoLinkBinaryTree tlBinTree = new TwoLinkBinaryTree<>("root");
		TwoLinkBinaryTree.TreeNode bt1 = tlBinTree.addNode(tlBinTree.root(), "two_left", true);
		TwoLinkBinaryTree.TreeNode bt2 = tlBinTree.addNode(tlBinTree.root(), "two_right", false);
		TwoLinkBinaryTree.TreeNode bt3 = tlBinTree.addNode(bt2, "three_right", false);
		TwoLinkBinaryTree.TreeNode bt4 = tlBinTree.addNode(bt3, "four_right", false);
		System.out.println("bt2的左节点:" + tlBinTree.leftChild(bt2));
		System.out.println("bt2的右节点:" + tlBinTree.rightChild(bt2));
	}
}
此方法遍历树节点时效率不高,指定节点访问其父节点也比较困难。但是节省空间。

三、三叉链表存储

就是在二叉链表的基础上,再增加一个parent指针,指向该节点的父节点。

代码与(二)类似,只需要在class TreeNode中添加一个parent, 构造函数里也做出改变。还有就是在addNode()方法中,新的节点newNode的parent应用需要指向parent节点。您可以对照(二)自己编写,相信比看要容易理解。

此时三叉链表存储的二叉树既可以方便的向下访问节点,也可以方便的向上访问节点。


参考: 《疯狂java 突破程序员基本功的16课》

以上就是这篇的内容。如果有错误的地方或者有可以改进指出,请您指出,谢谢!

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