左子,右兄弟表达树

时间:2013-12-03 12:59:26

标签: c++ algorithm recursion tree

我完全坚持我的一项任务,可以使用一些帮助。

我们正在实现一个树类,它存储和评估二进制表达式。以下是一个这样的表达式的示例:(MAJ(AND 3 4)(OR 1 2 3)(NOT 5)4)。这是指这棵树: sample tree

树以LCRS表示形式提供给我们,我们正在构建一个类来对其执行各种操作,例如查找树中的最大数字,查找特定操作符的编号或打印原始文件导致给定树的公式。我已经弄清楚所有这些,通过几个小时的头部刮擦和用于跟踪大型递归函数的纸叠。

但我无法弄清楚最后需要的功能,即

bool evaluate(const vector<bool> &values, Tree_Node* p)

此函数使用bool向量提供的真值来评估表达式(例如,如果值[3]和值[4]都为真,则(AND 3 4)将为真)。我花了很多时间寻找答案,并认为我正在使用表达式树,但在LCRS表示中无法找到它的工作原理。

我无法弄清楚递归。我把它分成了所有可能的情况,但是当你考虑不同的运算符时,有很多这样的情况。为了保持简洁,我会说我的主要问题是:

如何评估每个子树?例如,在LCRS树中,

 AND
 /
3
 \
  4

我看到我需要深入到4,意识到它是最简单的(基础)情况,然后开始递归到AND,但是当我回来时我看不到如何获得正确的值到AND。我需要额外的参数吗?也许传递下来的操作,或使用某种下面的额外指针?

//一些澄清的东西

四个运营商是:

  • MAJ - 如果大多数输入为真,则返回true
  • AND - 逻辑AND(如果两个输入都为真,则为true)
  • OR - 逻辑OR
  • NOT - 否定

(假设SO上的人理解并且,或者&amp; not。)

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 :: int,就像Counts一样。再次感谢那些帮助我解决这个问题的人。函数switch_help在给定字符串时返回switch语句的相应数字(例如switch_help("MAJORITY") == 2)。

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())];
    }

}

2 个答案:

答案 0 :(得分: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数组中的索引。

答案 1 :(得分:0)

表达式树的递归评估包括:

  • 评估树的每个节点

反过来要求:

  • 将操作应用于子项列表(操作数)。

现在,让我们尝试稍微系统化。考虑一下您的操作 - NOTANDORMAJ - 并考虑如何计算操作数列表中每个值的值。具体来说,我们需要了解哪些列表?在所有情况下,以下两个数据就足够了(只有一个是必要的):

  • 列表的值中有多少是TRUE

  • 列表的值中有多少是FALSE

(或者,第二个可能是“列表中有多少个值?”,这显然是等价的。)

如果AND个孩子的数量为0,则TRUEFALSE;如果OR个孩子的数量为0,则FALSETRUE;如果MAJ个孩子的数量大于TRUE个孩子的数量,则TRUEFALSE。 (NOT对操作数列表至少有两种推广,或者您可以将其限制为TRUE子项数为0且FALSE子项数为0的情况。 1。)

现在,假设我们已经将列表缩减为这两个值nTRUEnFALSE,并且我们想要在列表中添加一个新元素(这将是递归步骤。)可以我们这样做?明显。如果新元素为TRUE,我们会增加nTRUE;如果新元素为FALSE,我们会增加nFALSE。瞧!

在C ++中,表示一对值的标准方法是std::pair。使用它,我们可以表达我们的递归减少如下:

typedef std::pair<int, int> Counts;

Counts count(Tree_Node* node) {
  if (!node) return Counts{0, 0};
  Counts rest = count(node->right_sibling);
  if (evaluate(node))
    return Counts{rest.first + 1, rest.second};
  else
    return Counts{rest.first, rest.second + 1};
}

假设evaluate返回一个布尔值。但是,如果evaluate返回Counts个对象,使用{1,0}表示true,{0,1}表示false,我们可以使这更加优雅。然后我们可以写:

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 Counts{0, 0};
  else
    return evaluate(node) + count(node->right_sibling);
}

在这两种情况下,我都忽略了evaluate的定义,这需要根据运营商的不同而有所不同。但我希望evaluate(node)使用count(node->left_child)的方式很明显。