1.需求分析 2.功能設計 基本功能 基本功能 拓展功能 拓展功能 高級功能 高級功能 3.設計實現 4.代碼說明 遞歸查重 遞歸查重 // //代碼功能:遞歸實現二叉樹查重 // //實現思路: //1、先判斷兩個算式答案相不相同,不相同則算式一定不相同。 //2、然後對兩式計算左右兩邊的值是否能 ...
1.需求分析
1. 使用 -n 參數控制生成題目的個數,例如 Myapp.exe -n 10 -o Exercise.txt 將生成10個題目。 2. 使用 -r 參數控制題目中數值(自然數、真分數和真分數分母)的範圍,例如 Myapp.exe -r 10 將生成10以內(不包括10)的四則運算題目。該參數可以設置為1或其他自然數。該參數必須給定,否則程式報錯並給出幫助信息。 3. 生成的題目中如果存在形如e1 ÷ e2的子表達式,那麼其結果應是真分數。 4. 每道題目中出現的運算符個數不超過3個。 5. 程式一次運行生成的題目不能重覆,即任何兩道題目不能通過有限次交換+和×左右的算術表達式變換為同一道題目。例如,23 + 45 = 和45 + 23 = 是重覆的題目,6 × 8 = 和8 × 6 = 也是重覆的題目。3+(2+1)和1+2+3這兩個題目是重覆的,由於+是左結合的,1+2+3等價於(1+2)+3,也就是3+(1+2),也就是3+(2+1)。但是1+2+3和3+2+1是不重覆的兩道題,因為1+2+3等價於(1+2)+3,而3+2+1等價於(3+2)+1,它們之間不能通過有限次交換變成同一個題目。 生成的題目存入執行程式的當前目錄下的Exercises.txt文件,格式如下: 1. 四則運算題目1 2. 四則運算題目2 …… 其中真分數在輸入輸出時採用如下格式,真分數五分之三表示為3/5,真分數二又八分之三表示為2’3/8。 6. 在生成題目的同時,計算出所有題目的答案,並存入執行程式的當前目錄下的Answers.txt文件,格式如下: 1. 答案1 2. 答案2 特別的,真分數的運算如下例所示:1/6 + 1/8 = 7/24。 7. 程式應能支持一萬道題目的生成。 8. 程式支持對給定的題目文件和答案文件,判定答案中的對錯併進行數量統計,並會輸出所有題目中重覆的題目,輸入參數如下: Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt -o Grade.txt 統計結果輸出到文件Grade.txt,格式如下: Correct: 5 (1, 3, 5, 7, 9) Wrong: 5 (2, 4, 6, 8, 10) Repeat:2 RepeatDetail: (1) 2,45+32 Repeat 3,32+45 (2) 5,3+(2+1) Repeat 7,1+2+3 解釋: Correct: 5 ----5道題目正確,正確的題號 1,3,5,7,9 Wrong:5 -----5道題目錯誤,錯誤的題號 2,4,6,8,10 Repeat:2 2---組題目重覆 (1) 第一組 題號2,題目 45+32 與題號3的題目重覆,題號3為 32+45 (2)第二組 題號5,題目 3+(2+1) 與題號7的題目重覆,題號7為 1+2+3 其中“:”後面的數字5表示對/錯的題目的數量,括弧內的是對/錯題目的編號。為簡單起見,假設輸入的題目都是按照順序編號的符合規範的題目。
2.功能設計
-
基本功能
a.生成多個四則運算算式
b.校驗計算結果
-
拓展功能
a.四則運算含3個以內運算符
b.能輸出直觀的題目(合理去括弧)
c.計算結果為真分數
d.計算的數字有規定範圍
e.生成的算式合法(不含除以0或分母為0)
f.對計算結果進行通分化簡
g.批改支持將丟失的題目添加進錯誤
-
高級功能
a.支持任意多個運算符
b.二叉樹查重
3.設計實現
question:
-->Calculation.java:根據二叉樹根節點計算答案,包含化簡
-->CreateQuestion.java:生成二叉樹算式,包含查重及合法校驗
-->Number.java:數的存儲格式,包含整數部分、分子部分、分母部分
start:
-->OutQuestion.java:輸入參數操作、文件輸入輸出操作
tree:
-->BinaryTree.java:二叉樹,包含轉化為String算式的函數
-->TreeNode:樹的節點
4.代碼說明
-
遞歸查重
//---------------------------------------------------------------------------------- //代碼功能:遞歸實現二叉樹查重 //---------------------------------------------------------------------------------- //實現思路: //1、先判斷兩個算式答案相不相同,不相同則算式一定不相同。 //2、然後對兩式計算左右兩邊的值是否能匹配上。 //3、若能,將其子節點匹配後遞歸回1再比較,重覆123操作,直到結束或返回不同。 //---------------------------------------------------------------------------------- private boolean isRepeat(List<BinaryTree> questions, BinaryTree ques) { // 先按答案查重節省時間,再詳細對比 for (BinaryTree bt : questions) { if (bt.getAnswer() == ques.getAnswer()) { if (isRepeatTree(bt.getRootNode(), ques.getRootNode())) { System.out.println("重覆:【" + bt.getQuestion() + "】【" + ques.getQuestion() + "】");// 輸出重覆問題 return true; } } } return false; } private boolean isRepeatTree(TreeNode tnode1, TreeNode tnode2) {// 兩棵樹詳細對比查重 if (tnode1.getData().equals(tnode2.getData())) {// 當前節點相同 if (tnode1.getLeftTreeNode() == null && tnode2.getLeftTreeNode() == null) { // 兩樹均無下節點 return true; } String l1, r1, l2, r2; l1 = Calculation.calculation(tnode1.getLeftTreeNode()).getData(); r1 = Calculation.calculation(tnode1.getRightTreeNode()).getData(); l2 = Calculation.calculation(tnode2.getLeftTreeNode()).getData(); r2 = Calculation.calculation(tnode2.getRightTreeNode()).getData(); if (!(l1.equals(r1) && l2.equals(r2) && l1.equals(l2))) {// 兩個節點的左右節點計算值不全相同,非(a,a)和(a,a)這種組合 if (l1.equals(l2) && r1.equals(r2)) { // 同一邊的相同 return isRepeatTree(tnode1.getLeftTreeNode(), tnode2.getLeftTreeNode()) && isRepeatTree(tnode1.getRightTreeNode(), tnode2.getRightTreeNode()); } else if (l1.equals(r2) && r1.equals(l2)) { // 不同邊的相同 return isRepeatTree(tnode1.getLeftTreeNode(), tnode2.getRightTreeNode()) && isRepeatTree(tnode1.getRightTreeNode(), tnode2.getLeftTreeNode()); } return false;// 不相同 } else { // 像(2,2)和(2,2)這種組合 return (isRepeatTree(tnode1.getLeftTreeNode(), tnode2.getLeftTreeNode()) && isRepeatTree(tnode1.getRightTreeNode(), tnode2.getRightTreeNode()) || isRepeatTree( tnode1.getLeftTreeNode(), tnode2.getRightTreeNode()) && isRepeatTree(tnode1.getRightTreeNode(), tnode2.getLeftTreeNode()));// 有一種完全匹配即可 } } else { return false; } }View Code
-
輸出算式
//------------------------------------- //功能:根據二叉樹根節點輸出算式 //------------------------------------- public String toString() { // 中序遍歷輸出算式帶括弧 String result = inorderTraversal(rootNode); return result; } private String inorderTraversal(TreeNode tnode) { // 中序遍歷遞歸演算法,預設左右節點同時存在或同時不存在 if (tnode.getLeftTreeNode() != null && tnode.getRightTreeNode() != null) { return addBracketsL(tnode, tnode.getLeftTreeNode()) + tnode.getData() + addBracketsR(tnode, tnode.getRightTreeNode());// addBrackets添加括弧前會判斷 } else { return tnode.getData(); } } private String addBracketsL(TreeNode f, TreeNode c) { // 遞歸路徑:inorderTraversal->addBracketsL(或R)->inorderTraversal if ((f.getData() == "*" || f.getData() == "÷") && (c.getData() == "+" || c.getData() == "-")) { return "(" + inorderTraversal(c) + ")"; } else { return inorderTraversal(c); } } private String addBracketsR(TreeNode f, TreeNode c) { if ((f.getData() == "*" || f.getData() == "÷") && (c.getData() == "+" || c.getData() == "-")) { return "(" + inorderTraversal(c) + ")"; } else if (f.getData() == "-" && (c.getData() == "+" || c.getData() == "-")) { // 減號後面的加減號要加括弧,如:1-(2+3)、1-(2-3) return "(" + inorderTraversal(c) + ")"; } else if (f.getData() == "÷" && (c.getData() == "*" || c.getData() == "/")) { // 除號後面的乘除號要加括弧,如:1÷(2x3)、1÷(2÷3) return "(" + inorderTraversal(c) + ")"; } else { return inorderTraversal(c); } }View Code
5.測試運行
-
生成題目及答案
-
批改答案(刪除問題行仍可判錯)
-
二叉樹同構查重(生成時查重,若重覆則捨棄,輸出的題目無重覆的)
-
生成多個算式
備註:代碼寫得比較趕,並未大量測試
完整代碼:點擊跳轉