左子右兄弟表达式树
Posted
技术标签:
【中文标题】左子右兄弟表达式树【英文标题】:Left-child, right-sibling expression tree 【发布时间】:2013-12-03 12:59:25 【问题描述】:我完全被我的一项任务困住了,需要一些帮助。
我们正在实现一个树类,它存储和评估二进制表达式。这是一个这样的表达式的示例:(MAJ (AND 3 4) (OR 1 2 3) (NOT 5) 4 )。这是指这棵树:
树以 LCRS 表示形式提供给我们,我们正在构建一个类来对其执行各种操作,例如查找树中的最大数、查找特定运算符的编号或打印原始导致给定树的公式。我已经弄清楚了所有这些,通过数小时的头疼和用于追踪大型递归函数的成堆纸张。
但我不知道最后一个需要的函数是
bool evaluate(const vector<bool> &values, Tree_Node* p)
此函数使用 bool 向量提供的真值计算表达式(例如,如果 values[3] 和 values[4] 都为真,则 (AND 3 4) 为真)。我花了很多时间寻找答案,并认为我对表达式树有所了解,但找不到任何关于它在 LCRS 表示中如何工作的信息。
我只是无法弄清楚递归。我把它分成了所有可能的情况,但是当你考虑到不同的运营商时,会有很多情况。为了保持简洁,我会说我的主要问题是:
如何评估每个子树?例如,在 LCRS 树中,
AND
/
3
\
4
我看到我需要回到 4,意识到这是最简单的(基本)情况,然后开始递归回到 AND,但是当我回来时我不知道如何获得正确的值到 AND。我需要一个额外的参数还是什么?也许传递操作,或者使用某种后续的额外指针?
//一些澄清的东西
四个运算符是:
MAJ - 如果大多数输入为真,则返回真 AND - 逻辑与(如果两个输入都为真,则为真) OR - 逻辑或 NOT - 否定(假设 SO 上的人理解 and,or,¬。)
Node 结构是一个典型的结构:
struct Tree_Node
std::string data;
Tree_Node* left_child;
Tree_Node* right_sibling;
除了分配的附加功能外,Tree 类也是标准的, 如有必要,我可以发布代码,但它具有您期望的树的操作,并且可以正确编译和测试,问题仅与此功能有关。
我在这个和我的其他家庭作业之间敲打我的桌子......哦,CS专业的生活。任何帮助,一如既往,非常感谢。
编辑:
非常感谢所有帮助过我的人。我在这里得到的所有答案确实帮助我弄清楚了事情。有时它有助于获得一个新的视角。另外, std::pair 对我来说是新的!有趣的是,因为我有一个自己的模板类,它做同样的事情......猜猜是时候让那个家伙退休了!
等我完成作业后,我会把我的功能和分析贴在这里,以备日后参考。
编辑:
正如所承诺的,完成的功能。 count_children 在功能上与 rici 制定的 count 函数相同,而 TruthValues 是一个 std::pair 整数,如 Counts。再次感谢那些帮助我解决这个问题的人。当给定一个字符串(例如switch_help("MAJORITY") == 2
)时,函数 switch_help 会为 switch 语句返回适当的数字。
bool BooleanFormula::evaluate(const vector<bool> & values, Tree_Node* p)
TruthValues truth_vals = TruthValues(0,0);
int control = switch_help(p->data);
switch (control)
case 1: //p->data = "MAJORITY"
truth_vals = count_children(values, p->left_child);
return (truth_vals.first > truth_vals.second);
//return true if there are more trues than falses
case 2: //p->data = "AND"
truth_vals = count_children(values, p->left_child);
return (truth_vals.second == 0);
//return true if there are no falses
case 3: //p->data = "OR"
truth_vals = count_children(values, p->left_child);
return (truth_vals.first >= 1);
//return true if there is at least one true
case 4: //p->data = "NOT"
return !(evaluate(values, p->left_child));
//return the inverse of what is obtained by evaluating the subtree
default: //p->data = some number
//in this case, we're just at a node with an index in it
return values[atoi((p->data).c_str())];
【问题讨论】:
提示:递归评估函数的结果应该是一个值列表,而不是(必然)单个值。 好吧,它最终必须返回一个布尔值,尽管我明白你在说什么。我一直在玩弄这个想法——如果我能得到一个向量或包含 3 和 4 的东西,我想这会很有帮助。如果我要添加某种列表作为函数的参数,并通过引用传递它,那么...我可以在到达底部时添加值并在备份的路上构建该列表?这可能行得通,但我会遇到多个递归调用查看同一个列表的问题吗? ***函数调用根上的递归 case,断言返回列表仅包含一个元素,并将其返回。其他一切都返回一个列表。它不会通过引用传入列表。您可以在之后进行一些优化,但首先要让它发挥作用。 从实现叶子案例开始,然后逐步向上。并遵循@Sneftel 的建议。 对不起,我还是有点糊涂。你是说我应该编写一个返回列表的辅助函数(或者对于不同的运算符可能不止一个)?那么,当原始函数具有完整的列表时,只有一个元素,可以返回该列表中的一个值吗? 【参考方案1】:如果我理解结构,在每个子树上,您都有位于根的运算符,第一个操作数作为根的左子节点,随后的操作数作为右链接 第一个操作数,对吗?
那么评估的一般方案如下所示:
bool evaluate(const vector<bool> &values, Tree_Node* p)
bool result;
switch (p->op)
case BASE:
return values[p->index];
...
case AND:
result = true;
for (op = p->left; op != nullptr; op = op->right)
result &= evaluate (values, op);
return result;
...
(这里我假设叶子包含value
数组中的索引。
【讨论】:
【参考方案2】:表达式树的递归求值涉及:
评估树的每个节点这又需要:
对子项(操作数)列表应用操作。现在,让我们尝试稍微系统化。考虑您拥有的操作——NOT
、AND
、OR
和MAJ
——并考虑如何通过操作数列表计算每个操作数的值。具体来说,我们需要了解有关列表的哪些信息?在所有情况下,以下两个数据就足够了(只有一种情况都是必要的):
列表中有多少值是TRUE
?
列表中有多少值是FALSE
?
(或者,第二个可能是“列表中有多少个值?”,这显然是等价的。)
那么AND
是TRUE
当且仅当FALSE
孩子的数量是0; OR
是 FALSE
当且仅当 TRUE
孩子的数量为 0; MAJ
是 TRUE
,如果 TRUE
孩子的数量大于 FALSE
孩子的数量。 (NOT
至少有两个泛化到操作数列表,或者您可以将其限制为 TRUE
子代数为 0 且FALSE
子代数为 1 的情况。)
现在,假设我们已经将列表缩减为这两个值,nTRUE
和 nFALSE
,并且我们想要向列表中添加一个新元素(这将是递归步骤。)我们可以这样做吗?明显地。如果新元素是TRUE
,我们增加nTRUE
;如果新元素是FALSE
,我们增加nFALSE
。瞧!
在 C++ 中,表示一对值的标准方式是std::pair
。使用它,我们可以如下表达我们的递归归约:
typedef std::pair<int, int> Counts;
Counts count(Tree_Node* node)
if (!node) return Counts0, 0;
Counts rest = count(node->right_sibling);
if (evaluate(node))
return Countsrest.first + 1, rest.second;
else
return Countsrest.first, rest.second + 1;
假设evaluate
返回一个布尔值。但是如果评估返回一个Counts
对象,我们可以使这更加优雅,使用1,0
表示真,0,1
表示假。然后我们可以写:
typedef std::pair<int, int> Counts;
Counts operator+(const Counts& a, const Counts& b)
return a.first + b.first, a.second + b.second;
Counts count(Tree_Node* node)
if (!node)
return Counts0, 0;
else
return evaluate(node) + count(node->right_sibling);
在这两种情况下,我都省略了evaluate
的定义,它需要根据运营商而有所不同。但我希望evaluate(node)
使用count(node->left_child)
的方式很明显。
【讨论】:
以上是关于左子右兄弟表达式树的主要内容,如果未能解决你的问题,请参考以下文章