C++ 具有过约束类的梦魇表达式树
我无意中让我的学生过度训练了一个用于解决以下问题的共享课堂。我意识到这可能是这个网站的用户可能喜欢的一个问题 第一个团队/函数getNodes使用带符号整数和四个运算+、-、*、和/或表示前缀表达式的字符串,并使用类节点生成相应的以null结尾的令牌链接列表,令牌通过“右”指针链接 第二个团队/函数getTree接受一个类似的字符串,将其传递给getNodes,并将结果节点重新链接为表达式树 第三个团队/函数evaluate接受一个类似的字符串,将其传递给getTree,并计算结果表达式树以形成答案 下面是过度约束的exptree.h。这个问题必须通过只编写上面定义的三个函数来解决,不需要额外的函数C++ 具有过约束类的梦魇表达式树,c++,expression-trees,friend,C++,Expression Trees,Friend,我无意中让我的学生过度训练了一个用于解决以下问题的共享课堂。我意识到这可能是这个网站的用户可能喜欢的一个问题 第一个团队/函数getNodes使用带符号整数和四个运算+、-、*、和/或表示前缀表达式的字符串,并使用类节点生成相应的以null结尾的令牌链接列表,令牌通过“右”指针链接 第二个团队/函数getTree接受一个类似的字符串,将其传递给getNodes,并将结果节点重新链接为表达式树 第三个团队/函数evaluate接受一个类似的字符串,将其传递给getTree,并计算结果表达式树以形成
#ifndef EXPTREE_H_
#define EXPTREE_H_
using namespace std;
enum Ops{ADD, SUB, MUL, DIV, NUM};
class Node {
private:
int num;
Ops op;
Node *left, *right;
public:
friend Node *getNodes(string d);
friend Node *getTree(string d);
friend int evaluate (string);
};
int evaluate(string d);
Node *getNodes(string d);
Node *getTree(string d);
#endif
唯一可以使用的库是这些
#include <iostream>
#include <vector>
#include <string>
#include "exptree.h"
#包括
#包括
#包括
#包括“exptree.h”
对于那些担心我的学生的人,我今天将指出,仅仅几个位置更合适的函数就可以轻松解决这个问题。我知道表达式树可以编码有理数,而不仅仅是整数。我今天也要指出这一点
这是我根据他们的规格给他们的驱动程序
#include <iostream>
#include <string>
#include "exptree.h"
using namespace std;
void test(string s, int target) {
int result = evaluate(s);
if (result == target)
cout << s << " correctly evaluates to " << target << endl;
else
cout << s << "(" << result
<< ") incorrectly evaluates to " << target << endl;
}
int main() {
test("42", 42);
test("* - / 4 2 1 42", 42);
test("* - / -4 +2 -1 2", -2);
test("* - / -4 +2 -1 2 ", -2);
test("* 9 6", 54);
return 0;
}
#包括
#包括
#包括“exptree.h”
使用名称空间std;
无效测试(字符串s,int目标){
int结果=评估;
如果(结果==目标)
cout在这些约束条件下编写getNodes
和getTree
函数非常简单,因此我跳过了前面有趣的部分。您自然会递归地计算表达式树,但这不是一个选项,因为eval函数只接受一个字符串。当然,您可以对将main树转换为前缀表达式并递归调用eval,但这太愚蠢了
首先,我将表达式树转换为后缀表达式,使用显式堆栈作为穷人的递归,然后使用标准操作数堆栈对其求值
#include <iostream>
#include <vector>
#include <string>
using namespace std;
#include "exptree.h"
int evaluate(string d){
Node* tree = getTree(d);
//convert tree to postfix for simpler evaluation
vector<Node*> node_stack;
node_stack.push_back(tree);
Node postfix_head;
Node* postfix_tail = &postfix_head;
while(node_stack.size() > 0){
Node* place = node_stack.back();
if(place->left == 0){
if(place->right == 0){
postfix_tail->right = place;
node_stack.pop_back();
} else {
node_stack.push_back(place->right);
place->right = 0;
}
} else {
node_stack.push_back(place->left);
place->left = 0;
}
}
//evaluate postfix
Node* place = postfix_head.right;
vector<int> stack;
while(place != 0){
if(place->op != NUM){
int operand_a, operand_b;
operand_b = stack.back();
stack.pop_back();
operand_a = stack.back();
stack.pop_back();
switch(place->op){
case ADD:
stack.push_back(operand_a + operand_b);
break;
case SUB:
stack.push_back(operand_a - operand_b);
break;
case MUL:
stack.push_back(operand_a * operand_b);
break;
case DIV:
stack.push_back(operand_a / operand_b);
break;
}
} else {
stack.push_back(place->num);
}
place = place->right;
}
return stack.back();
}
#包括
#包括
#包括
使用名称空间std;
#包括“exptree.h”
整数求值(字符串d){
节点*tree=getTree(d);
//将树转换为后缀以简化计算
向量节点栈;
节点\u堆栈。向后推\u(树);
节点后缀_头;
节点*postfix\u tail=&postfix\u head;
while(node_stack.size()>0){
Node*place=Node_stack.back();
如果(位置->左==0){
如果(位置->右侧==0){
后缀尾->右=位置;
node_stack.pop_back();
}否则{
节点堆栈。向后推(位置->右侧);
地点->右=0;
}
}否则{
节点栈。推回(放置->左);
位置->左=0;
}
}
//计算后缀
Node*place=postfix\u head.right;
矢量叠加;
while(place!=0){
如果(位置->操作!=NUM){
int操作数_a,操作数_b;
操作数_b=stack.back();
stack.pop_back();
操作数_a=stack.back();
stack.pop_back();
开关(位置->操作){
案例补充:
堆栈。向后推(操作数a+操作数b);
打破
个案小组:
stack.push_back(操作数_a-操作数_b);
打破
案例MUL:
堆栈。推回(操作数a*操作数b);
打破
案件组:
堆栈。推回(操作数a/操作数b);
打破
}
}否则{
堆栈。向后推_(位置->数量);
}
地点=地点->右侧;
}
返回stack.back();
}
我认为“没有额外的函数”是一个太苛刻的要求。实现例如getTree
的最简单方法可能是递归的,它需要定义一个额外的函数
Node* relink(Node* start) // builds a tree; returns the following node
{
if (start->op == NUM)
{
Node* result = start->right;
start->left = start->right = NULL;
return result;
}
else
{
start->left = start->right;
start->right = relink(start->left);
return relink(start->right);
}
}
Node* getTree(string d)
{
Node* head = getNodes(d);
relink(head);
return head;
}
我可以通过使用显式堆栈(通过std::vector实现)实现递归,但这很难看也很难懂(除非你希望学生们严格地练习)。关于它的价值,这是我在发布问题之前编写的解决方案
#include <iostream>
#include <vector>
#include "exptree.h"
using namespace std;
Node *getNodes(string s) {
const int MAXINT =(int)(((unsigned int)-1) >> 1), MININT = -MAXINT -1;
Node *list;
int sign, num;
s += " "; // this simplifies a lot of logic, allows trailing white space to always close off an integer
list = (Node *) (num = sign = 0);
for (int i=0; i<s.size(); ++i) {
char c = s[i]; // more efficient and cleaner reference to the current character under scrutiny
if (isdigit(c)) {
if (sign == 0) sign = 1; // if sign not set, then set it. A blank with a sign==0 now signifies a blank that can be skipped
num = 10*num + c - '0';
} else if (((c=='+') || (c=='-')) && isdigit(s[i+1])) { // another advantage of adding blank to string above so don't need a special case
sign = (c=='+') ? 1 : -1;
} else if ( !isspace(c) && (c != '+') && (c != '-') && (c != '*') && (c != '/')) {
cout << "unexpected character " << c << endl;
exit(1);
} else if (!isspace(c) || (sign != 0)) { // have enough info to create next Node
list->left = (list == 0) ? (list = new Node) : (list->left->right = new Node); // make sure left pointer of first Node points to last Node
list->left->right = 0; // make sure list is still null terminated
list->left->op = (c=='+' ? ADD : (c=='-' ? SUB : (c=='*' ? MUL : (c=='/' ? DIV : NUM)))); // choose right enumerated type
list->left->num = (list->left->op==NUM) ? sign*num : MININT; // if interior node mark number for evaluate function
num = sign = 0; // prepare for next Node
}
}
return list;
}
Node *getTree(string s) {
Node *nodes = getNodes(s), *tree=0, *root, *node;
vector<Node *> stack;
if (nodes == 0) return tree;
root = tree = nodes;
nodes = nodes->right;
for (node=nodes; node != 0; node=nodes) {
nodes = nodes->right;
if (root->op != NUM) { // push interior operator Node on stack til time to point to its right tree
stack.push_back(root);
root = (root->left = node); // set interior operator Node's left tree and prepare to process that left tree
} else {
root->left = root->right = 0; // got a leaf number Node so finish it off
if (stack.size() == 0) break;
root = stack.back(); // now pop operator Node off the stack
stack.pop_back();
root = (root->right = node); // set its left tree and prepare to process that left tree
}
}
if ((stack.size() != 0) || (nodes != 0)) {
cout << "prefix expression has missing or extra terms" << endl;
exit(1);
}
return tree;
}
int evaluate(string s) {
// MININT is reserved value signifying operator waiting for a left side value, low inpact since at edge of representable integers
const int MAXINT =(int)(((unsigned int)-1) >> 1), MININT = -MAXINT -1;
Node *tree = getTree(s);
vector<Node *> stack;
int v = 0; // this is value of a leaf node (a number) or the result of evaluating an interior node
if (tree == 0) return v;
do {
v = tree->num;
if (tree->op != NUM) {
stack.push_back(tree);
tree = tree->left; // prepare to process the left subtree
} else while (stack.size() != 0) { // this while loop zooms us up the right side as far as we can go (till we come up left side or are done)
delete tree; // done with leaf node or an interior node we just finished evaluating
tree = stack.back(); // get last interior node from stack
if (tree->num == MININT) { // means returning up left side of node, so save result for later
tree->num = v;
tree = tree->right; // prepare to evaluate the right subtree
break; // leave the "else while" for the outer "do while" which handles evaluating an expression tree
} else { // coming up right side of an interior node (time to calculate)
stack.pop_back(); // all done with interior node
v = tree->op==ADD ? tree->num+v : (tree->op==SUB ? tree->num-v : (tree->op==MUL ? tree->num*v : tree->num/v)) ;
}
}
} while (stack.size() != 0);
return v;
}
#包括
#包括
#包括“exptree.h”
使用名称空间std;
节点*getNodes(字符串s){
常量int MAXINT=(int)((无符号int)-1>>1),MININT=-MAXINT-1;
节点*列表;
int符号,num;
s+=“”;//这简化了许多逻辑,允许尾随空格始终关闭整数
列表=(节点*)(num=sign=0);
对于(inti=0;ileft=root->right=0;//获得了一个叶编号节点,所以请完成它
如果(stack.size()==0)中断;
root=stack.back();//现在将操作符节点从堆栈中弹出
stack.pop_back();
root=(root->right=node);//设置其左树并准备处理该左树
}
}
if((stack.size()!=0)| |(nodes!=0)){
cout 1),MININT=-MAXINT-1;
节点*tree=getTree(s);
矢量叠加;
int v=0;//这是叶节点的值(一个数字)或对内部节点求值的结果
如果(tree==0)返回v;
做{
v=树->数量;
如果(树->操作!=NUM){
堆叠。推回(树);
tree=tree->left;//准备处理左子树
}else-while(stack.size()!=0){//这个while循环将我们从右侧放大到我们能走的地方(直到我们从左侧放大或完成)
delete tree;//完成了叶节点或内部节点的计算
tree=stack.back();//进入最后一个