栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java学习(21-25天, 树与二叉树)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java学习(21-25天, 树与二叉树)

闵老板の帖子: 日撸 Java 三百行(21-30天,树与二叉树)

2021年秋季第六周周报内容


1. 二叉树的深度遍历的递归实现

树是数据结构中的重中之重, 特别是二叉树.许多实际问题抽象出来的数据结构往往是二叉树形式, 即使是一般的树也能简单地转换为二叉树, 而且二叉树的存储结构及其算法都较为简单, 因此二叉树显得特别重要. 二叉树特点是每个结点最多只能有两棵子树, 且有左右之分.

这里用递归的方法为二叉树写了几个方法:

1. 遍历. 包括前序, 中序, 后序遍历.

2. 计算深度的方法.

3. 计算总结点数的方法.

package datastructure.tree;

public class BinaryTree {

	// The value;
	char value;
	
	// The left child;
	BinaryTree leftChild;
	
	// The right child;
	BinaryTree rightChild;
	
	
	public BinaryTree(char paraValue) {
		value = paraValue;
		leftChild = null;
		rightChild = null;
	}// Of one constructor
	
	
	public static BinaryTree manualConstructTree() {
		// The root of the tree.
		BinaryTree resultTree = new BinaryTree('A');
		
		// The other nodes.
		BinaryTree tempTreeB = new BinaryTree('B');
		BinaryTree tempTreeC = new BinaryTree('C');
		BinaryTree tempTreeD = new BinaryTree('D');
		BinaryTree tempTreeE = new BinaryTree('E');
		BinaryTree tempTreeF = new BinaryTree('F');
		BinaryTree tempTreeG = new BinaryTree('G');
		
		// link them.
		resultTree.leftChild = tempTreeB;
		resultTree.rightChild = tempTreeC;
		tempTreeB.rightChild = tempTreeD;
		tempTreeC.leftChild = tempTreeE;
		tempTreeD.leftChild = tempTreeF;
		tempTreeD.rightChild = tempTreeG;

		
		return resultTree;
	}// Of manualConstrucTree
	
	
	public void preOrder() {
		System.out.print(value + " ");
		if(leftChild != null) {
			leftChild.preOrder();
		}// Of if
		
		if(rightChild != null) {
			rightChild.preOrder();
		}// Of if
	}// Of preOrder
	
	
	public void inOrder() {
		if(leftChild != null) {
			leftChild.inOrder();
		}// Of if
		System.out.print(value + " ");
		
		if(rightChild != null) {
			rightChild.inOrder();
		}// Of if
	}// Of inOrder.
	
	
	public void postOrder() {
		if(leftChild != null) {
			leftChild.postOrder();
		}// Of if
		if(rightChild != null) {
			rightChild.postOrder();
		}// Of if
		
		System.out.print(value + " ");
	}// Of postOrder.
	
	
	public int getDepth() {
		if(leftChild == null && rightChild == null) {
			return 1;
		}// Of if 
		int tempLeftDepth = 0, tempRightDepth = 0;
		
		if(leftChild != null) {
			tempLeftDepth = leftChild.getDepth();
		}// Of if
		if(rightChild != null) {
			tempRightDepth = rightChild.getDepth();
		}// Of if
		
		if(tempLeftDepth >= tempRightDepth) {
			return tempLeftDepth+1;
		}// Of if 
		else{
			return tempRightDepth+1;
		}// Of else
	}// Of getDepth
	
	
	public int getNumNodes() {
		if ((leftChild == null) && (rightChild == null)) {
			return 1;
		} // Of if

		int tempLeftNodes = 0, tempRightNodes = 0;
		if (leftChild != null) {
			tempLeftNodes = leftChild.getNumNodes();
		} // Of if
		
		if (rightChild != null) {
			tempRightNodes = rightChild.getNumNodes();
		} // Of if

		return tempLeftNodes + tempRightNodes + 1;
	}// Of getNumNodes.
	
	
	public static void main(String args[]) {
		BinaryTree tempTree = manualConstructTree();
		System.out.println("Pre-order visit:");
		tempTree.preOrder();
		System.out.println("rIn-order visit:");
		tempTree.inOrder();
		System.out.println("rPost-order visit:");
		tempTree.postOrder();

		System.out.println("rThe depth is: " + tempTree.getDepth());
		System.out.println("The number of nodes is: " + tempTree.getNumNodes());
	}// Of main

}// Of BinaryCharTree

输出:

Pre-order visit:
A B D F G C E 
In-order visit:
B F D G A E C 
Post-order visit:
F G D B E C A 
The depth is: 4
The number of nodes is: 7
2. 二叉树的存储

我们可以完全满二叉树的角度广度优先遍历的角度来考虑这个问题: 每个节点都有一个 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]

我们用队列的方式来定义. 与之前不同的是, 这次定义用的是Object类型, 它可以接收任意类型的数据.

package datastructure;

public class CircularObjectQueue {
	
	// The total space. But the actual sapce will be one less.
	public static final int MAX_SIZE = 10;
	
	// The data;
	Object[] data;
	
	// The index of the head.
	int front;
	
	// The index of the tail.
	int rear;
	
	
	
	
	public CircularObjectQueue() {
		data = new Object[MAX_SIZE];
		front = 0;
		rear = 0;
	}// Of the constructor
	
	
	public boolean enqueue(Object paraValue) {
		if(front == (rear + 1) % MAX_SIZE) {
			// System.out.println("Queue full");
			return false;
		}
		
		data[rear % MAX_SIZE] = paraValue;
		rear++;
		
		return true;
	}// Of enqueue
	
	
	public Object dequeue() {
		Object resultValue;
		if(front == rear) {
			// System.out.println("The queue is empty, dequeue fail");
			return null;
		}// Of if
		
		resultValue = data[front % MAX_SIZE];
		front++;
		return resultValue;
		
	}// Of dequeue
	
	
	public String toString() {
		String resultString = "";
		if(front == rear) {
			return "empty";
		}// Of if
		
		for(int i = front; i < rear - 1; i++) {
			resultString += data[i % MAX_SIZE] + ", ";
		}// Of for i 
		resultString += data[(rear-1) % MAX_SIZE];
		
		return resultString;
	}// Of toString
}// Of class CircularObjectQueue

下面是老师的代码实现. 将内容位置应在之前写的BinaryTree.java中.

需要注意的是,节点的索引从0开始. 那么节点的左孩子的索引是其索引的二倍+1, 右孩子是其索引的二倍+2.

	
	char[] valuesArray;

	
	int[] indicesArray;

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

		BinaryCharTree tempTree = (BinaryCharTree) 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 = (BinaryCharTree) tempQueue.dequeue();
			tempIndex = tempIntQueue.dequeue();
		} // Of while
	}// Of toDataArrays

	
	public static void main(String args[]) {
		BinaryCharTree tempTree = manualConstructTree();
		System.out.println("rnPreorder visit:");
		tempTree.preOrderVisit();
		System.out.println("rnIn-order visit:");
		tempTree.inOrderVisit();
		System.out.println("rnPost-order visit:");
		tempTree.postOrderVisit();

		System.out.println("rnrnThe depth is: " + tempTree.getDepth());
		System.out.println("The number of nodes is: " + tempTree.getNumNodes());

		tempTree.toDataArrays();
		System.out.println("The values are: " + Arrays.toString(tempTree.valuesArray));
		System.out.println("The indices are: " + Arrays.toString(tempTree.indicesArray));
	}// Of main	

输出:

Preorder visit:
A B D F G C E 
In-order visit:
B F D G A E C 
Post-order visit:
F G D B E C A 
The depth is: 4
The number of nodes is: 7
The values are: [A, B, C, D, E, F, G]
The indices are: [0, 1, 2, 4, 5, 9, 10]
3. 使用具有通用性的队列

我们刚刚已经知道, Object可以用来接收任意类型数据, 那么我们在定义队列的时候, 也不再需要专门去为不同的数据类型创建不同的队列了.

我们可以直接这样定义数据:

Object[] data;

但是在使用的时候, 需要利用强制类型转换使其变成本身的类别. 比如

tempTree = (BinaryCharTree) tempQueue.dequeue();

4. 二叉树的建立

这里是二叉树的另一种构造方法, 通过输入相应的valuesArray、indicesArray来构造. 

主要有三个步骤:

1. 创建一个Binary类型的数组来存储我们需要的所有节点.

2. 采用双重循环的方式判断是否为某个节点的左右孩子. 这里设计的十分巧妙, 从除根节点外的第一个节点出发, 判断其与之前的节点是否满足为其左右孩子的条件, 若满足条件, 则用之前创建好的Binary类型节点链接起来, 然后跳出循环, 判断下一个节点, 以此直到所有节点判断完毕.

3. 链接完毕, 回到根节点.

	public BinaryTree(char[] paraValuesArray, int[] paraIndicesArray) {
		// Step 1. Use a sequential list to store all nodes.
		int tempNumNodes = paraValuesArray.length;
		BinaryTree[] tempAllNodes = new BinaryTree[tempNumNodes];
		for (int i = 0; i < tempNumNodes; i++) {
			tempAllNodes[i] = new BinaryTree(paraValuesArray[i]);
		} // Of for i

		// Step 2. link these nodes.
		for (int i = 1; i < tempNumNodes; i++) {
			for (int j = 0; j < i; j++) {
				System.out.println("indices " + paraIndicesArray[j] + " vs. " + paraIndicesArray[i]);
				if (paraIndicesArray[i] == paraIndicesArray[j] * 2 + 1) {
					tempAllNodes[j].leftChild = tempAllNodes[i];
					System.out.println("linking " + j + " with " + i);
					break;
				} else if (paraIndicesArray[i] == paraIndicesArray[j] * 2 + 2) {
					tempAllNodes[j].rightChild = tempAllNodes[i];
					System.out.println("linking " + j + " with " + i);
					break;
				} // Of if
			} // Of for j
		} // Of for i
		
		//Step 3. The root is the first node.
		value = tempAllNodes[0].value;
		leftChild = tempAllNodes[0].leftChild;
		rightChild = tempAllNodes[0].rightChild;
	}// Of the the third constructor
5. 二叉树深度遍历的栈实现 (中序) 5.1 构建通用栈

在之前写的stack.java中稍作修改, 并添加了.isEmpty()方法.

package datastructure.Stack;


public class ObjectStack {
	// The max depth of the stack.
	public static final int MAX_DEPTH = 10;

	// The actual depth of the stack.
	int depth;

	// The data.
	Object[] data;

	
	public ObjectStack() {
		depth = 0;
		data = new Object[MAX_DEPTH];
	}// Of the constructor

	
	public boolean push(Object paraObject) {
		if (depth == MAX_DEPTH) {
			System.out.println("The stack is full, push fail");
			return false;
		} // Of if
		data[depth] = paraObject;
		depth++;

		return true;
	}// Of push

	
	public Object pop() {
		if (depth == 0) {
			System.out.println("The stack is empty, pop fail");
			return '';
		} // Of if
		Object resultValue = data[depth - 1];
		depth--;

		return resultValue;
	}// Of pop

	
	public String toString() {
		String resultString = "";

		if (depth == 0) {
			return "empty";
		} // Of if

		for (int i = 0; i < depth; i++) {
			resultString += data[i];
		} // Of for i

		return resultString;
	}// Of toString
	
	
	public boolean isEmpty() {
		if(depth == 0) {
			return true;
		}// Of if
		
		return false;
	}// Of isEmpty
	
}// Of class ObjectStack
5.2 中序遍历

简单来说, 入左, 左空出左,入右...

    
	public void inOrderWithStack() {
		ObjectStack tempStack = new ObjectStack();
		BinaryTree tempNode = this;
		while (! tempStack.isEmpty() || tempNode != null) {
			if (tempNode != null) {
				tempStack.push(tempNode);
				tempNode = tempNode.leftChild;
			} else {
				tempNode = (BinaryTree) tempStack.pop();
				System.out.print(tempNode.value + " ");
				tempNode = tempNode.rightChild;
			} // Of if
		} // Of while
	}// Of inOrderVisit
6. 小结

看不懂就反复看, 或者画图画出来, 多敲几遍, 然后自己琢磨.

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/314405.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号