【问题标题】:How to build an and-or tree?如何构建与或树?
【发布时间】:2011-05-21 20:45:21
【问题描述】:

我需要一个支持“与”和“或”的树结构。例如,给定一个像ab|c(d|e) 这样的正则表达式,我想把它变成一棵树。

所以,起初我们有两个“或”分支......它可以向下 abc(d|e)。如果你沿着ab 分支前进,你会得到两个节点,a b(或a,然后是b,等等)。然后如果你沿着c(d|e) 分支下去,你会得到c (d|e),然后(d|e) 被分成d e

制作树形结构很容易,你只要有类似的东西

class Node {
    string element;
    Node[] children;
}

但是你怎么知道孩子应该是“anded”还是“ored”呢?我猜树的每一层都应该在“anding”和“oring”之间交替

这有意义吗?任何人都可以为此建议一个结构吗?


有些人建议将“运算符”存储在节点上,这很好,但是没有办法利用每个级别总是交替的事实或,和,或,和,... ?

编辑:不太清楚为什么人们一直认为这是一棵二叉树。 不是。我希望小代码 sn-p 能给你提示。这个例子只是碰巧只有2个分支。


目前倾向于这个:

abstract class Node { }

class DataNode : Node
{
    string data;
}

abstract class OpNode : Node
{
    Node[] children;
}

class OrNode : OpNode { }
class AndNode : OpNode { }

【问题讨论】:

  • 我认为交替的 AND / OR 级别只是这个例子的巧合。通常,您应该能够在同一级别上混合使用 AND 和 OR。看我的回答。
  • @mbeckish:是吗?你能提供一个你不会替代的例子吗?
  • 另外,您不需要子数组(除非您要支持 N 元运算符)。
  • @Ralph: a|b|c 是一个简单的例子。
  • 你是如何用树的每一层和或或仅制作 (a|b|c)(de) 的?

标签: c# data-structures


【解决方案1】:

考虑一个树结构,其中每个节点都代表一个布尔表达式,可以评估为真或假 - 在您的情况下是正则表达式(匹配或不匹配)。 树结构本身代表 AND 和 OR: 每条路由,从根节点开始,到没有其他子节点的节点结束,都是表达式的合取,代表 AND。 树

    A
   /
  B
 /
C

将代表 A AND B AND C。

当一个节点有超过 1 个子节点时,就会有一个 OR(析取),分支成多个路由:

    A
   / \
  B   D
 /
C

表示 A AND ((B AND C) OR D)

因此您甚至不需要将运算符存储在任何地方。

在您的示例中,您有表达式ab|c(d|e),因此没有要评估的公共根表达式;我建议这种情况下的根只是true,树看起来像:

   true
   / \
  A   C
 /   / \
B   D   E

如需 C# 中的自定义树类,请查看此处Tree data structure in C# 或搜索或制作您自己的。

【讨论】:

  • 我喜欢这个!!它消除了跟踪交替或运算符的需要,并且它干净且易于阅读。
【解决方案2】:
abstract class Node { }

class DataNode : Node {
    public string Data { get; }

    // details
}

class OperatorNode : Node {
    public Node Left { get; }
    public Node Right { get; }
    public BinaryOperator Operator { get; }

    // details
}

abstract class BinaryOperator { // details }

class Or : BinaryOperator { // details }
class And : BinaryOperator { // details }

【讨论】:

  • 那么,ab|c(d|e)这个例子怎么实现呢?
  • 另请注意,它不是二叉树,但很容易解决。 “数据”存储在哪里?只有叶子有数据......只需将其放在节点中并将其保留为空,除非它是叶子?
【解决方案3】:

这有什么问题吗:

enum Operation
{
    None,
    And,
    Or
}

class Node {
    string element;
    Node[] children;
    Operation operation;
}

编辑:

作为ab|c(d|e) 看起来像这样的示例:

Node root = new Node
        {
            operation = Operation.Or,
            children = new Node[]
            {
                new Node
                {
                    operation = Operation.And,
                    children = new Node[]
                    {
                          new Node{ element = "a" },
                          new Node{ element="b" }
                    }
                },
                new Node
                {
                    children = new Node[]
                    {
                        new Node{ element = "c"},
                        new Node
                        {
                            operation= Operation.Or,
                            children = new Node[]
                            {
                                new Node{ element= "d"},
                                new Node{element = "e"}
                            }
                        }
                    }
                }
            }
        };

【讨论】:

  • ab|c(d|e)这个例子怎么实现?
  • @mbeckish:不过我更喜欢你的方法,将我的一个 Node 类型分成两种类型(每个 new Node 将替换为 new OperatorNodenew ElementNode
【解决方案4】:

这样简单的事情怎么样:

class OrNode {
  string element;
  AndNode[] children;
} 

class AndNode {
  string element;
  OrNode[] children;
} 

每个班级都可以有自己的evaluate(),它可以根据需要对所有孩子进行AND或OR

您可能仍然希望有一个父超类,以便您的代码可以保存通用节点,而不必担心第一个是 AND 还是 OR。

【讨论】:

  • 这是迄今为止唯一可以强制交替的解决方案……而且很简单。我喜欢它。
【解决方案5】:

只是添加一个稍微不同的

interface Node
{
    // top level operations here
}

class OpNode : Node
{
    public Node Left { get; set; }
    public Node Right { get; set; }
}

class AndNode : OpNode
{
    public AndNode(Node left, Node right)
    {
        Left = left;
        Right = right;
    }
    public override string ToString()
    {
        return "(" + Left.ToString() + " & " + Right.ToString() + ")";
    }
}

class OrNode : OpNode
{
    public OrNode(Node left, Node right)
    {
        Left = left;
        Right = right;
    }
    public override string ToString()
    {
        return "(" + Left.ToString() + " | " + Right.ToString() + ")";
    }
}

class DataNode<T> : Node
{
    T _data;
    public DataNode(T data)
    {
        _data = data;
    }
    public override string ToString()
    {
        return _data.ToString();
    }
}

【讨论】:

  • 听起来像是 mbeckish 的解决方案,但您更进一步,还将操作节点拆分为两种不同的类型。
【解决方案6】:

你可以有两种类型的节点:操作符节点和变量节点。

你的树的叶子都是可变节点;所有其他节点都是操作员节点。

二元运算符节点将有两个子节点。一元运算符(如 NOT)节点将有 1 个子节点。

对于您的示例 ab|c(d|e):

      OR
  /         \
 AND       AND
 / \      /  \
a   b    c   OR
           /  \
          d    e

【讨论】:

  • 以编程方式,这似乎很容易实现。解析将涉及开始和最左边的叶子和遍历节点。
【解决方案7】:

我在几天前使用ANTLR 完成了这项工作。 ANTLR 为我提供了一个语法,它表示为您刚才描述的 AST 抽象语法树,它生成了可以处理该语法的 c# 代码。

它非常漂亮和优雅。这是few example

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-07
    • 1970-01-01
    • 1970-01-01
    • 2012-02-04
    • 2016-07-05
    • 1970-01-01
    • 2021-02-09
    • 2020-10-10
    相关资源
    最近更新 更多