20182320 2019-2020-1 《数据结构与面向对象程序设计》实验8报告
课程:《程序设计与数据结构》
班级: 1823
姓名: 郑力元
学号:20182320
实验教师:王志强
实验日期:2019年11月11日
必修/选修: 必修
1.实验内容
1.1
参考教材PP16.1,完成链树LinkedBinaryTree的实现(getRight,contains,toString,preorder,postorder)
用JUnit或自己编写驱动类对自己实现的LinkedBinaryTree进行测试,提交测试代码运行截图,要全屏,包含自己的学号信息
课下把代码推送到代码托管平台
1.2
基于LinkedBinaryTree,实现基于(中序,先序)序列构造唯一一棵二㕚树的功能,比如给出中序HDIBEMJNAFCKGL和后序ABDHIEJMNCFGKL,构造出附图中的树
用JUnit或自己编写驱动类对自己实现的功能进行测试,提交测试代码运行截图,要全屏,包含自己的学号信息
课下把代码推送到代码托管平台
1.3
自己设计并实现一颗决策树
提交测试代码运行截图,要全屏,包含自己的学号信息
课下把代码推送到代码托管平台
1.4
输入中缀表达式,使用树将中缀表达式转换为后缀表达式,并输出后缀表达式和计算结果(如果没有用树,正常评分。如果用到了树,即使有小的问题,也酌情给满分)
提交测试代码运行截图,要全屏,包含自己的学号信息
2. 实验过程及结果
2.1
第一步:补充实现书上的LinkedBinaryTree类,它需要编写一个BinaryTree接口,一个节点类,两个异常类,才能完整实现LinkedBinaryTree。
代码如下,这里将书上的ArrayIterator换成了ArrayList:
public class LinkedBinaryTree<T> implements BinaryTree<T>
{
protected BTNode<T> root;
public LinkedBinaryTree()
{
root = null;
}
public LinkedBinaryTree (T element)
{
root = new BTNode<T>(element);
}
public LinkedBinaryTree (T element, LinkedBinaryTree<T> left,
LinkedBinaryTree<T> right)
{
root = new BTNode<T>(element);
root.setLeft(left.root);
root.setRight(right.root);
}
public T getRootElement() throws Exception, EmptyCollectionException {
if (root == null)
throw new EmptyCollectionException ("Get root operation "
+ "failed. The tree is empty.");
return root.getElement();
}
public LinkedBinaryTree<T> getLeft() throws Exception, EmptyCollectionException {
if (root == null)
throw new EmptyCollectionException ("Get left operation "
+ "failed. The tree is empty.");
LinkedBinaryTree<T> result = new LinkedBinaryTree<T>();
result.root = root.getLeft();
return result;
}
public T find (T target) throws ElementNotFoundException {
BTNode<T> node = null;
if (root != null)
node = root.find(target);
if (node == null)
throw new ElementNotFoundException("Find operation failed. "
+ "No such element in tree.");
return node.getElement();
}
//返回大小
public int size()
{
int result = 0;
if (root != null)
result = root.count();
return result;
}
public LinkedBinaryTree<T> getRight() throws Exception, EmptyCollectionException {
if (root == null)
throw new EmptyCollectionException ("Get left operation "
+ "failed. The tree is empty.");
LinkedBinaryTree<T> result = new LinkedBinaryTree<T>();
result.root = root.getRight();
return result;
}
public boolean contains (T target) {
if (root.find(target)==null){
return false;
}
else {
return true;
}
}
public boolean isEmpty() {
if (root==null){
return true;
}
else {
return false;
}
}
//先序遍历
public ArrayList<T> preorder() {
ArrayList<T> iter = new ArrayList<T>();
if (root != null)
root.preorder (iter);
return iter;
}
//后续遍历
public ArrayList<T> postorder() {
ArrayList<T> iter = new ArrayList<T>();
if (root != null)
root.postorder (iter);
return iter;
}
public String toString() {
return super.toString();
}
}
第二步:编写测试代码,运行
2.2
第一步:修改上面的LinkedBinaryTree方法,将先序、中序和后序遍历都改成返回字符串,同时加入能够从上到下构造二叉树的方法:
public int findIndexInArray(char[] a, char x, int begin, int end) {
for(int i=begin;i<=end;i++) {
if(a[i] == x) {
return i;
}
}
return -1;
}
public void initTree(char[] preOrder, char[] inOrder) {
this.root = this.initTree(preOrder, 0, preOrder.length-1, inOrder, 0, inOrder.length-1);
}
public BTNode initTree(char[] preOrder, int start1, int end1, char[] inOrder, int start2, int end2) {
if(start1 > end1 || start2 > end2) {
return null;
}
//通过前序找到根结底
char rootData = preOrder[start1];
BTNode<Character> head = new BTNode(rootData);
//从中序遍历里找到根结点所在的位置
int rootIndex = findIndexInArray(inOrder, rootData, start2, end2);
//offSet代表左子树的长度-1(也就是中序遍历中,左子树最后一个元素的下标)
int offSet = rootIndex - start2 - 1;
//递归构建左子树
BTNode left = initTree(preOrder, start1+1, start1+1+offSet, inOrder, start2, start2+offSet);
//递归构建右子树
BTNode right = initTree(preOrder, start1+offSet+2, end1, inOrder, rootIndex+1, end2);
head.left = left;
head.right = right;
return head;
}
第二步:编写测试代码,运行,这里用后续遍历检验是否正常构建二叉树
2.3
第一步:编写好节点类和决策树类(包含主方法和构建与运行决策树的方法)
public static void buildDTree(){
root=new BTNode("大力帅吗?y/n");
BTNode<String> temp=root;
temp.left=new BTNode<>("你错了。");
temp.right=new BTNode<>("大力聪明吗?y/n");
temp=temp.right;
temp.left=new BTNode<>("你错了。");
temp.right=new BTNode<>("大力强吗?y/n");
temp=temp.right;
temp.left=new BTNode<>("你错了。");
temp.right=new BTNode<>("你说的都对了。");
}
public static void runDTree(BTNode root){
System.out.println(root.element);
if (root.left==null||root.right==null){
return;
}
while (true){
stringTokenizer=new StringTokenizer(scanner.nextLine());
String string=stringTokenizer.nextToken();
if (string.equals("y")){
runDTree(root.right);
break;
}
else if (string.equals("n")){
runDTree(root.left);
break;
}
else {
System.out.println("输入错误!重新输入。");
}
}
}
第二步:运行
2.4
第一步:编写中缀转后缀的类(包括主方法):
public static String infixToSuffix(String infix) {
Stack<Character> stack = new Stack<Character>();
String suffix = "";
int length = infix.length();
for (int i = 0; i < length; i++) {
Character temp;
char c = infix.charAt(i);
switch (c) {
// 忽略空格
case ' ':
break;
// 碰到'(',push到栈
case '(':
stack.push(c);
break;
// 碰到'+''-',将栈中所有运算符弹出,送到输出队列中
case '+':
case '-':
while (stack.size() != 0) {
temp = stack.pop();
if (temp == '(') {
stack.push('(');
break;
}
suffix += " " + temp;
}
stack.push(c);
suffix += " ";
break;
// 碰到'*''/',将栈中所有乘除运算符弹出,送到输出队列中
case '*':
case '/':
while (stack.size() != 0) {
temp = stack.pop();
if (temp == '(' || temp == '+' || temp == '-') {
stack.push(temp);
break;
} else {
suffix += " " + temp;
}
}
stack.push(c);
suffix += " ";
break;
// 碰到右括号,将靠近栈顶的第一个左括号上面的运算符全部依次弹出,送至输出队列后,再丢弃左括号
case ')':
while (stack.size() != 0) {
temp = stack.pop();
if (temp == '(')
break;
else
suffix += " " + temp;
}
// suffix += " ";
break;
//如果是数字,直接送至输出序列
default:
suffix += c;
}
}
//如果栈不为空,把剩余的运算符依次弹出,送至输出序列。
while (stack.size() != 0) {
suffix += " " + stack.pop();
}
return suffix;
}
第二步:运行
3. 实验过程中遇到的问题和解决过程
问题1:
原本是想基于LinkedBinaryTree的构造二叉树的方法,完成8.2的二叉树的构建,但是发现LinkedBinaryTree的原本构造方法是自下而上构造二叉树的,然而已知的先序和中序遍历能确定的根是在二叉树的最顶端
问题1解决:
自己加了一个自上而下的构造二叉树的方法,实现功能。
其他(感悟、思考等)
这一次实验主要考察我们对树的结构以及其特点的掌握,涉及到了树的构建、通过前序与中序构造树还有决策树的构建。虽然在最后一个中缀转后缀中我没能用树的结构来实现,但是通过栈和队列两种线性结构来实现这个功能难度依然不小,花费时间较多。这些编程实践能够让我们对一些经典的数据结构有更深刻的理解。