20202310 《数据结构与面向对象程序设计》实验八报告
20202310 《数据结构与面向对象程序设计》实验八报告
课程:《程序设计与数据结构》
班级: 2023
姓名: 肖衍豪
学号:20202310
实验教师:王志强
实验日期:2021年11月18日
必修/选修: 必修
一、实验内容
1.参考教材PP16.1,完成链树LinkedBinaryTree的实现(getRight,contains,toString,preorder,postorder)
用JUnit或自己编写驱动类对自己实现的LinkedBinaryTree进行测试,提交测试代码运行截图,要全屏,包含自己的学号信息
课下把代码推送到代码托管平台
2.基于LinkedBinaryTree,实现基于(中序,先序)序列构造唯一一棵二㕚树的功能,比如给出中序HDIBEMJNAFCKGL和后序ABDHIEJMNCFGKL,构造出附图中的树
用JUnit或自己编写驱动类对自己实现的功能进行测试,提交测试代码运行截图,要全屏,包含自己的学号信息
课下把代码推送到代码托管平台
3.自己设计并实现一颗决策树
提交测试代码运行截图,要全屏,包含自己的学号信息
课下把代码推送到代码托管平台
4.输入中缀表达式,使用树将中缀表达式转换为后缀表达式,并输出后缀表达式和计算结果(如果没有用树,正常评分。如果用到了树,即使有小的问题,也酌情给满分)
提交测试代码运行截图,要全屏,包含自己的学号信息
二、实验过程及结果
1.参考教材PP16.1,完成链树LinkedBinaryTree的实现(getRight,contains,toString,preorder,postorder)
用JUnit或自己编写驱动类对自己实现的LinkedBinaryTree进行测试,提交测试代码运行截图,要全屏,包含自己的学号信息
课下把代码推送到代码托管平台
(1)二叉树:
import java.util.ArrayList; import java.util.List; public class bintree { public bintree left; public bintree right; public bintree root; // 数据域 private Object data; // 存节点 public List<bintree> datas; public bintree(bintree left, bintree right, Object data){ this.left=left; this.right=right; this.data=data; } // 将初始的左右孩子值为空 public bintree(Object data){ this(null,null,data); } public bintree() { } public void creat(Object[] objs){ datas=new ArrayList<bintree>(); // 将一个数组的值依次转换为Node节点 for(Object o:objs){ datas.add(new bintree(o)); } // 第一个数为根节点 root=datas.get(0); // 建立二叉树 for (int i = 0; i <objs.length/2; i++) { // 左孩子 datas.get(i).left=datas.get(i*2+1); // 右孩子 if(i*2+2<datas.size()){//避免偶数的时候 下标越界 datas.get(i).right=datas.get(i*2+2); } } } //先序遍历 public void preorder(bintree root){ if(root!=null){ System.out.println(root.data); preorder(root.left); preorder(root.right); } } //中序遍历 public void inorder(bintree root){ if(root!=null){ inorder(root.left); System.out.println(root.data); inorder(root.right); } } // 后序遍历 public void afterorder(bintree root){ if(root!=null){ System.out.println(root.data); afterorder(root.left); afterorder(root.right); } } public static void main(String[] args) { bintree bintree=new bintree(); Object []a={2,4,5,7,1,6,12,32,51,22}; bintree.creat(a); bintree.preorder(bintree.root); } }
(2)测试结果:
2.基于LinkedBinaryTree,实现基于(中序,先序)序列构造唯一一棵二㕚树的功能,比如给出中序HDIBEMJNAFCKGL和后序ABDHIEJMNCFGKL,构造出附图中的树
用JUnit或自己编写驱动类对自己实现的功能进行测试,提交测试代码运行截图,要全屏,包含自己的学号信息。
(1)二叉树依据先序序列和中序序列
package com.mytest.mymain; import java.util.Arrays; //定义节点 class BinaryTree{ public int value; public BinaryTree leftNode; public BinaryTree rightNode; BinaryTree(int x) { value = x; } } public class ConstrontTree { public static void main(String[] args) throws Exception { int[] preSort={1,2,4,7,3,5,6,8}; int[] inSort=new int[]{4,7,2,1,5,3,8,6}; BinaryTree root=startBuildTree(preSort,inSort); } //01递归生成树 private static BinaryTree startBuildTree(int[] preSort,int[] inSort) throws Exception { //异常判断 if(preSort==null || inSort==null){ return null; } if(preSort.length!=inSort.length){ throw new Exception("不满足条件的非法输入!"); } BinaryTree root=null; for(int i=0;i<inSort.length;i++){ if(inSort[i]==preSort[0]){ root=new BinaryTree(preSort[0]); System.out.println(preSort[0]); root.leftNode=startBuildTree( Arrays.copyOfRange(preSort, 1, i+1), Arrays.copyOfRange(inSort, 0, i)); root.rightNode=startBuildTree( Arrays.copyOfRange(preSort, i+1, preSort.length), Arrays.copyOfRange(inSort, i+1, inSort.length)); } } return root; } }
(2)测试:
import java.util.Scanner; public class treetext { public static void main(String[] args) { System.out.println("请输入一个二叉树的前序序列"); Scanner scan = new Scanner(System.in); String a = scan.nextLine(); String[] a2 = a.split(" "); System.out.println("请输入一个二叉树的中序序列"); String b = scan.nextLine(); String[] b2 = b.split(" "); creattree ct = new creattree(); System.out.println(ct.buildTree(a2,b2)); } }
(3)测试截图:
3.自己设计并实现一颗决策树
提交测试代码运行截图,要全屏,包含自己的学号信息
课下把代码推送到代码托管平台
package demo; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; public class DicisionTree { public static void main(String[] args) throws Exception { System.out.print("脚本之家测试结果:"); String[] attrNames = new String[] { "AGE", "INCOME", "STUDENT", "CREDIT_RATING" }; // 读取样本集 Map<Object, List<Sample>> samples = readSamples(attrNames); // 生成决策树 Object decisionTree = generateDecisionTree(samples, attrNames); // 输出决策树 outputDecisionTree(decisionTree, 0, null); } /** * 读取已分类的样本集,返回Map:分类 -> 属于该分类的样本的列表 */ static Map<Object, List<Sample>> readSamples(String[] attrNames) { // 样本属性及其所属分类(数组中的最后一个元素为样本所属分类) Object[][] rawData = new Object[][] { { "<30 ", "High ", "No ", "Fair ", "0" }, { "<30 ", "High ", "No ", "Excellent", "0" }, { "30-40", "High ", "No ", "Fair ", "1" }, { ">40 ", "Medium", "No ", "Fair ", "1" }, { ">40 ", "Low ", "Yes", "Fair ", "1" }, { ">40 ", "Low ", "Yes", "Excellent", "0" }, { "30-40", "Low ", "Yes", "Excellent", "1" }, { "<30 ", "Medium", "No ", "Fair ", "0" }, { "<30 ", "Low ", "Yes", "Fair ", "1" }, { ">40 ", "Medium", "Yes", "Fair ", "1" }, { "<30 ", "Medium", "Yes", "Excellent", "1" }, { "30-40", "Medium", "No ", "Excellent", "1" }, { "30-40", "High ", "Yes", "Fair ", "1" }, { ">40 ", "Medium", "No ", "Excellent", "0" } }; // 读取样本属性及其所属分类,构造表示样本的Sample对象,并按分类划分样本集 Map<Object, List<Sample>> ret = new HashMap<Object, List<Sample>>(); for (Object[] row : rawData) { Sample sample = new Sample(); int i = 0; for (int n = row.length - 1; i < n; i++) sample.setAttribute(attrNames[i], row[i]); sample.setCategory(row[i]); List<Sample> samples = ret.get(row[i]); if (samples == null) { samples = new LinkedList<Sample>(); ret.put(row[i], samples); } samples.add(sample); } return ret; } static Object generateDecisionTree( Map<Object, List<Sample>> categoryToSamples, String[] attrNames) { // 如果只有一个样本,将该样本所属分类作为新样本的分类 if (categoryToSamples.size() == 1) return categoryToSamples.keySet().iterator().next(); // 如果没有供决策的属性,则将样本集中具有最多样本的分类作为新样本的分类,即投票选举出分类 if (attrNames.length == 0) { int max = 0; Object maxCategory = null; for (Entry<Object, List<Sample>> entry : categoryToSamples .entrySet()) { int cur = entry.getValue().size(); if (cur > max) { max = cur; maxCategory = entry.getKey(); } } return maxCategory; } // 选取测试属性 Object[] rst = chooseBestTestAttribute(categoryToSamples, attrNames); // 决策树根结点,分支属性为选取的测试属性 Tree tree = new Tree(attrNames[(Integer) rst[0]]); // 已用过的测试属性不应再次被选为测试属性 String[] subA = new String[attrNames.length - 1]; for (int i = 0, j = 0; i < attrNames.length; i++) if (i != (Integer) rst[0]) subA[j++] = attrNames[i]; // 根据分支属性生成分支 @SuppressWarnings("unchecked") Map<Object, Map<Object, List<Sample>>> splits = /* NEW LINE */(Map<Object, Map<Object, List<Sample>>>) rst[2]; for (Entry<Object, Map<Object, List<Sample>>> entry : splits.entrySet()) { Object attrValue = entry.getKey(); Map<Object, List<Sample>> split = entry.getValue(); Object child = generateDecisionTree(split, subA); tree.setChild(attrValue, child); } return tree; } static Object[] chooseBestTestAttribute( Map<Object, List<Sample>> categoryToSamples, String[] attrNames) { int minIndex = -1; // 最优属性下标 double minValue = Double.MAX_VALUE; // 最小信息量 Map<Object, Map<Object, List<Sample>>> minSplits = null; // 最优分支方案 // 对每一个属性,计算将其作为测试属性的情况下在各分支确定新样本的分类需要的信息量之和,选取最小为最优 for (int attrIndex = 0; attrIndex < attrNames.length; attrIndex++) { int allCount = 0; // 统计样本总数的计数器 // 按当前属性构建Map:属性值->(分类->样本列表) Map<Object, Map<Object, List<Sample>>> curSplits = /* NEW LINE */new HashMap<Object, Map<Object, List<Sample>>>(); for (Entry<Object, List<Sample>> entry : categoryToSamples .entrySet()) { Object category = entry.getKey(); List<Sample> samples = entry.getValue(); for (Sample sample : samples) { Object attrValue = sample .getAttribute(attrNames[attrIndex]); Map<Object, List<Sample>> split = curSplits.get(attrValue); if (split == null) { split = new HashMap<Object, List<Sample>>(); curSplits.put(attrValue, split); } List<Sample> splitSamples = split.get(category); if (splitSamples == null) { splitSamples = new LinkedList<Sample>(); split.put(category, splitSamples); } splitSamples.add(sample); } allCount += samples.size(); } // 计算将当前属性作为测试属性的情况下在各分支确定新样本的分类需要的信息量之和 double curValue = 0.0; // 计数器:累加各分支 for (Map<Object, List<Sample>> splits : curSplits.values()) { double perSplitCount = 0; for (List<Sample> list : splits.values()) perSplitCount += list.size(); // 累计当前分支样本数 double perSplitValue = 0.0; // 计数器:当前分支 for (List<Sample> list : splits.values()) { double p = list.size() / perSplitCount; perSplitValue -= p * (Math.log(p) / Math.log(2)); } curValue += (perSplitCount / allCount) * perSplitValue; } // 选取最小为最优 if (minValue > curValue) { minIndex = attrIndex; minValue = curValue; minSplits = curSplits; } } return new Object[] { minIndex, minValue, minSplits }; } static void outputDecisionTree(Object obj, int level, Object from) { for (int i = 0; i < level; i++) System.out.print("|-----"); if (from != null) System.out.printf("(%s):", from); if (obj instanceof Tree) { Tree tree = (Tree) obj; String attrName = tree.getAttribute(); System.out.printf("[%s = ?]\n", attrName); for (Object attrValue : tree.getAttributeValues()) { Object child = tree.getChild(attrValue); outputDecisionTree(child, level + 1, attrName + " = " + attrValue); } } else { System.out.printf("[CATEGORY = %s]\n", obj); } } static class Sample { private Map<String, Object> attributes = new HashMap<String, Object>(); private Object category; public Object getAttribute(String name) { return attributes.get(name); } public void setAttribute(String name, Object value) { attributes.put(name, value); } public Object getCategory() { return category; } public void setCategory(Object category) { this.category = category; } public String toString() { return attributes.toString(); } } static class Tree { private String attribute; private Map<Object, Object> children = new HashMap<Object, Object>(); public Tree(String attribute) { this.attribute = attribute; } public String getAttribute() { return attribute; } public Object getChild(Object attrValue) { return children.get(attrValue); } public void setChild(Object attrValue, Object child) { children.put(attrValue, child); } public Set<Object> getAttributeValues() { return children.keySet(); } } }
4.输入中缀表达式,使用树将中缀表达式转换为后缀表达式,并输出后缀表达式和计算结果(如果没有用树,正常评分。如果用到了树,即使有小的问题,也酌情给满分)
提交测试代码运行截图,要全屏,包含自己的学号信息
package com.rf.springboot01.dataStructure.stack.reversePolishNotation; import java.util.ArrayList; import java.util.List; import java.util.Stack; public class infixConvertorSuffix { public static void main(String[] args) { // 定义一个中缀表达式, 示例:1+((2+3)×4)-5 =>1 2 3 + 4 × + 5 – String expression = "1+((2+3)*4)-5"; //中缀表达式转成list 示例:1+((2+3)×4)-5 =>[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5] List<String> infixConvertorList = infixConvertorList(expression); System.out.println("中缀表达式对应的list======"+infixConvertorList); List<String> suffixList=infixListConvertorSuffixList(infixConvertorList); System.out.println("后缀表达式对应的list======"+suffixList); int result=computer(suffixList); System.out.println("中缀表达式计算的结果为:"+result); } public static List<String> infixConvertorList(String str){ List<String> list=new ArrayList(); String s;//用于多位数的拼接 char c;// 每遍历到一个字符,就放入到c int i=0; do{ if((c=str.charAt(i))<48 || (c=str.charAt(i))>57 ){//如果非数字 list.add(c+"");//字节转字符串,在添加到list i++; }else{//如果是数字,考虑多位数拼接 s=""; while(i<str.length() && (c=str.charAt(i))>=48 && (c=str.charAt(i))<=57){ s+=c; i++; } list.add(s); } }while(i<str.length()); return list; } public static List<String> infixListConvertorSuffixList(List<String> ls){ //定义一个运算符栈 Stack<String> s1=new Stack<>(); //定义一个储存中间结果的栈s2,由于s2栈没有pop操作,而且后面我们还需要逆序输出, // 因此直接使用 List<String> 代替stack<String> List<String> s2 = new ArrayList<String>(); for(String item:ls){ if(item.matches("\\d+")){//如果是数字 s2.add(item);//入储存中间结果的栈s2 }else if(item.equals("(")){//如果是左括号,直接入运算符栈s1 s1.push(item); }else if(item.equals(")")){//如果是右括号,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃 while(!s1.peek().equals("(")){ s2.add(s1.pop()); } s1.pop();//!!! 将左括号弹出 s1栈, 消除小括号 }else{//当item的优先级小于等于s1栈顶运算符, 将s1栈顶的运算符弹出并加入到s2中, while(s1.size() != 0 && Operation.getOperationValue(item)<=Operation.getOperationValue(s1.peek())){ s2.add(s1.pop()); } //还需要将item压入栈 s1.push(item); } } //将s1中剩余的运算符依次弹出并加入s2 while(s1.size() != 0) { s2.add(s1.pop()); } return s2; //注意因为是存放到List, 因此按顺序输出就是对应的后缀表达式对应的List } public static int computer(List<String> list){ // 创建给栈, 只需要一个栈即可 Stack<String> stack =new Stack<>(); //遍历 for(String data:list){ if(data.matches("\\d+")){//匹配的是多位数 stack.push(data);//入栈 }else{//是符号,先pop出两个数,并运算,再入栈 int num2=Integer.parseInt(stack.pop()); int num1=Integer.parseInt(stack.pop()); int result=0; if(data.equals("+")){ result=num1+num2; }else if(data.equals("-")){ result=num1-num2; }else if(data.equals("*")){ result=num1*num2; }else if(data.equals("/")){ result=num1/num2; }else{ throw new RuntimeException("运算符有误"); } //把结果result转字符串后入栈 stack.push(result+""); } } //最后留在stack中的数据是运算结果 return Integer.parseInt(stack.pop()); } } class Operation{ private static int ADD=1;//默认加法的优先级为1 private static int SUB=1;//默认减法的优先级为1 private static int MUL=2;//默认乘法的优先级为2 private static int DIV=2;//默认除法的优先级为2 public static int getOperationValue(String oper){ int result=0; switch (oper){ case "+": result=ADD; break; case "-": result=SUB; break; case "*": result=MUL; break; case "/": result=DIV; break; default: System.out.println("不存在该运算符:"+oper); break; } return result; } }