第 22 天: 二叉树的存储

 二叉树的存储并非一个简单的问题. 引用 (指针) 是无法存储到文件里面的.
 但我们从广度优先遍历的角度来思考似乎要简单些,每个节点都有一个 name 及其在二叉树中的位置. 令根节点的位置为 0; 则第 2 层节点的位置依次为 1 至 2; 第 3 层节点的位置依次为 3 至 6. 以此类推.
把昨天那个例子所对应的二叉树画出来, 我们有两种方法:

  1. 空使用 0 来表示, 可以用一个向量来存储:
    [a, b, c, 0, d, e, 0, 0, 0, f, g]
    优点: 仅需要一个向量, 简单直接.
    缺点: 对于实际的二叉树, 很多子树为空, 导致大量的 0 值.
    2.刘知鑫指出: 应使用压缩存储方式, 即将节点的位置和值均存储. 可表示为两个向量:
    [0, 1, 2, 4, 5, 9, 10]
    [a, b, c, d, e, f, g]

由于递归程序涉及变量的作用域问题 (额, 这个真的有点头疼), 我们需要使用层次遍历的方式, 获得以上的几个向量. 为此, 需要用到队列. 我们先灌点水, 把队列的代码再写一遍, 但这次装的是对象 (的引用)

package datastructure.queue;

/*
 * Circle object queue.
@author Fan Min minfanphd@163.com

@learner ChenXun
*/
public class CircleObjectQueue {

	/**
	 * The total space. One space can never be used.
	 */
	public static final int TOTAL_SPACE = 10;

	/**
	 * The data.
	 */
	Object[] data;

	/**
	 * The index of the head.
	 */
	int head;

	/**
	 * The index of the tail.
	 */
	int tail;

	/**
	 ******************* 
	 * The constructor
	 ******************* 
	 */
	public CircleObjectQueue() {
		data = new Object[TOTAL_SPACE];
		head = 0;
		tail = 0;
	}// Of the first constructor

	/**
	 *********************
	 * Enqueue.
	 * 
	 * @param paraValue The value of the new node.
	 *********************
	 */
	public void enqueue(Object paraValue) {
		if ((tail + 1) % TOTAL_SPACE == head) {
			System.out.println("Queue full.");
			return;
		} // Of if

		data[tail % TOTAL_SPACE] = paraValue;
		tail++;
	}// Of enqueue

	/**
	 *********************
	 * Dequeue.
	 * 
	 * @return The value at the head.
	 *********************
	 */
	public Object dequeue() {
		if (head == tail) {
			// System.out.println("No element in the queue");
			return null;
		} // Of if

		Object resultValue = data[head];

		head++;

		return resultValue;
	}// Of dequeue

	/**
	 *********************
	 * Overrides the method claimed in Object, the superclass of any class.
	 *********************
	 */
	public String toString() {
		String resultString = "";

		if (head == tail) {
			return "empty";
		} // Of if

		for (int i = head; i < tail; i++) {
			resultString += data[i % TOTAL_SPACE] + ", ";
		} // Of for i

		return resultString;
	}// Of toString

	/**
	 *********************
	 * The entrance of the program.
	 * 
	 * @param args Not used now.
	 *********************
	 */
	public static void main(String args[]) {
		CircleObjectQueue tempQueue = new CircleObjectQueue();
	}// Of main

}// Of CircleObjectQueue``


  1. 数据转换的代码很短, 但要理解还是比较花时间. 反正我写的时候比较辛苦.
  2. 用了两个队列.
  3. 实际上涵盖了广度优先 (或称层次) 遍历.
  4. 节点左儿子的编号是其编号 2 倍再加 1; 右儿子则应加 2.
  5. main 前面的保留, 只是为了读者方便与前面的程序对标.
  6. 需要 import datastructure.queue.*;
    代码如下:
package datastructure.tree;

import java.util.Arrays;
import datastructure.queue.*;

/**
 * Binary tree with char type elements.
 * 
 * @author Fan Min minfanphd@163.com
 * @learner ChenXun
 */
public class BinaryCharTree1 {

	// 字符中的值
	char value;
	
	// 左孩子
	BinaryCharTree1 leftChild;
	
	// 右孩子
	BinaryCharTree1 rightChild;

	// 构造方法
	public BinaryCharTree1(char paraName) {
		value = paraName;
		leftChild = null;
		rightChild = null;
	}

	// 手动建立一个二叉树
	public static BinaryCharTree1 manualConstructTree() {
		// 第一步,构造一个只有一个节点的树。
		BinaryCharTree1 resultTree = new BinaryCharTree1('a');

		// 第二步,构造所有的节点,第一个节点是根节点
		// BinaryCharTreeNode tempTreeA = resultTree.root;
		BinaryCharTree1 tempTreeB = new BinaryCharTree1('b');
		BinaryCharTree1 tempTreeC = new BinaryCharTree1('c');
		BinaryCharTree1 tempTreeD = new BinaryCharTree1('d');
		BinaryCharTree1 tempTreeE = new BinaryCharTree1('e');
		BinaryCharTree1 tempTreeF = new BinaryCharTree1('f');
		BinaryCharTree1 tempTreeG = new BinaryCharTree1('g');

		// 第三步,连接所有的节点
		resultTree.leftChild = tempTreeB;
		resultTree.rightChild = tempTreeC;
		tempTreeB.rightChild = tempTreeD;
		tempTreeC.leftChild = tempTreeE;
		tempTreeD.leftChild = tempTreeF;
		tempTreeD.rightChild = tempTreeG;

		return resultTree;
	}

	// 前序遍历
	public void preOrderVisit() {
		System.out.print("" + value + " ");

		if (leftChild != null) {
			leftChild.preOrderVisit();
		}

		if (rightChild != null) {
			rightChild.preOrderVisit();
		}
	}

	// 中序遍历
	public void inOrderVisit() {
		if (leftChild != null) {
			leftChild.inOrderVisit();
		}

		System.out.print("" + value + " ");

		if (rightChild != null) {
			rightChild.inOrderVisit();
		}
	}

	// 后序遍历
	public void postOrderVisit() {
		if (leftChild != null) {
			leftChild.postOrderVisit();
		}

		if (rightChild != null) {
			rightChild.postOrderVisit();
		}

		System.out.print("" + value + " ");
	}

	// 获取二叉树的深度。
	// 返回深度。如果只有一个节点,即根节点,则为 1。
	public int getDepth() {
		// 是叶子
		if ((leftChild == null) && (rightChild == null)) {
			return 1;
		}

		// 做孩子的深度
		int tempLeftDepth = 0;
		if (leftChild != null) {
			tempLeftDepth = leftChild.getDepth();
		}

		// 右孩子的深度
		int tempRightDepth = 0;
		if (rightChild != null) {
			tempRightDepth = rightChild.getDepth();
		}

		// 深度递增1
		if (tempLeftDepth >= tempRightDepth) {
			return tempLeftDepth + 1;
		} else {
			return tempRightDepth + 1;
		}
	}

	// 获取节点数,并返回节点
	public int getNumNodes() {
		// 是叶子节点
		if ((leftChild == null) && (rightChild == null)) {
			return 1;
		}

		// 左孩子的节点
		int tempLeftNodes = 0;
		if (leftChild != null) {
			tempLeftNodes = leftChild.getNumNodes();
		}

		// 右孩子的节点
		int tempRightNodes = 0;
		if (rightChild != null) {
			tempRightNodes = rightChild.getNumNodes();
		}

		// 节点总数
		return tempLeftNodes + tempRightNodes + 1;
	}

	/**
	 * 节点的值根据广度优先遍历。
	 */
	char[] valuesArray;

	/**
	 * 完整二叉树中的索引
	 */
	int[] indicesArray;

	/**
	 ********************
	 * 将树转换为数据数组,包括 char 数组和 int 数组。完整二叉树中的索引。 他的结果储存在两个成员变量当中
	 * 
	 * @see #valuesArray 这个放char
	 * @see #indicesArray 这个放int
	 *********************
	 */
	public void toDataArrays() {
		// Initialize arrays.
		int tempLength = getNumNodes();

		valuesArray = new char[tempLength];
		indicesArray = new int[tempLength];
		int i = 0;

		// Traverse and convert at the same time.
		CircleObjectQueue tempQueue = new CircleObjectQueue();
		tempQueue.enqueue(this);
		CircleIntQueue tempIntQueue = new CircleIntQueue();
		tempIntQueue.enqueue(0);

		BinaryCharTree1 tempTree = (BinaryCharTree1) tempQueue.dequeue();
		int tempIndex = tempIntQueue.dequeue();
		while (tempTree != null) {
			valuesArray[i] = tempTree.value;
			indicesArray[i] = tempIndex;
			i++;

			if (tempTree.leftChild != null) {
				tempQueue.enqueue(tempTree.leftChild);
				tempIntQueue.enqueue(tempIndex * 2 + 1);
			} // Of if

			if (tempTree.rightChild != null) {
				tempQueue.enqueue(tempTree.rightChild);
				tempIntQueue.enqueue(tempIndex * 2 + 2);
			} // Of if

			tempTree = (BinaryCharTree1) tempQueue.dequeue();
			tempIndex = tempIntQueue.dequeue();
		} // Of while
	}// Of toDataArrays

	// 主函数入口
	public static void main(String args[]) {

		BinaryCharTree1 tempTree1 = manualConstructTree();
		System.out.println("\r\nPreorder visit:");
		tempTree1.preOrderVisit();
		System.out.println("\r\nIn-order visit:");
		tempTree1.inOrderVisit();
		System.out.println("\r\nPost-order visit:");
		tempTree1.postOrderVisit();

		System.out.println("\r\n\r\nThe depth is: " + tempTree1.getDepth());
		System.out.println("The number of nodes is: " + tempTree1.getNumNodes());

		tempTree1.toDataArrays();

		System.out.println("The values are: " + Arrays.toString(tempTree1.valuesArray));
		System.out.println("The indices are: " + Arrays.toString(tempTree1.indicesArray));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值