将前缀表达式树向量转换为ORF/Karva表示法表达式树向量

Convert a prefix expression tree vector to a ORF/Karva notation expression tree vector

本文关键字:向量 表达式 Karva 表示 转换 前缀 ORF      更新时间:2023-10-16

好吧,这有点棘手。

我有一堆采用表达式树的代码,比如:

((a + b)/(c + d) + sqrt(e))

以前缀形式存储在向量中(我使用C++,但我只需要算法):

+(/(+(a,b),+(c,d)),sqrt(e))//括号只是为了帮助你阅读。每个运算符和终端都是向量中的一个元素。

现在有另一种表示表达式树的方法,称为ORF形式的

(论文第三页:http://arxiv.org/ftp/cs/papers/0102/0102027.pdf)

在这种形式中,你表示树就像从左到右、从上到下阅读树一样。

((a + b)/(c + d) + sqrt(e))现在变成:

+/sqrt++eabcd

我一直没能做的是创建一个可以转换的算法:

+/sqrt++eabcd/ORF进入:
+(/(+(a,b),+(c,d)),sqrt(e))//前缀

到目前为止,我所拥有的只是一些代码,可以在不同级别上获得树的宽度:

bool ConvertPrefixToORF(const std::vector<Node> & individual,
                              std::vector<Node> & ETindividual){
bool all_terminal = false;
int breadth;
int breadthOfDepth[individual.size()];
int depthCounter = 0;
breadthOfDepth[0] = 1;
//Resize only once.
ETindividual.reserve(individual.size());
while (all_terminal == false) {
    //Reset breadth
    breadth = 0;
    //Iterate over next level and calculate depth of level after that.
    for (int i = 0; i < breadthOfDepth[depthCounter]; i++) {
        all_terminal = true;
        //If the individual is a function...
        if (individual[current+i].isFunction()) {
            //Get individual from i'th item at this depth
            function f = individual[current + i];
            //Increment breadth of next level with arity
            breadth += f->getArity();
            //if a single function is found
            all_terminal = false;
        }
    }
    //Go down a level in the tree.
    depthCounter++;
    //Set the breadth of that tree depth:
    breadthOfDepth[depthCounter] = breadth;
}
}

提前感谢您的帮助!这让我头疼。哦,这对性能至关重要:(

我的策略是构建解析树,然后先深入它以生成前缀表示法。

您可以使用队列从ORF构建解析树。当您遇到每个运算符(或术语)时,请将其作为队列头的运算符的子项。当队列顶端的节点有足够的子节点时,将其从队列中弹出。

在你的例子中。。。从+开始,并将其推送到队列中(初始元素的特殊情况)。

接下来处理/。由于队列头部的+没有子级(但需要两个),因此将/附加到+作为其第一个子级,并将/推送到队列中。所以现在队列看起来像:

+/

树看起来像

      +
    /   .
  .   .

其中"."是等待填写的元素。

接下来是sqrt。由于+位于队列的头部,并且还没有两个子级,因此将sqrt附加到+,并将sqrt推送到队列上。所以现在队列看起来像:

+/sqrt

树看起来像

      +
    /   sqrt
  .   .   .

接下来是第二个CCD_ 18。队列的头是第一个+,但现在它已经有了所有的子级。所以把它从队列中弹出。队列的下一个元素是/,它还没有子元素,所以这个+成为它的子元素并进入队列的后面。队列现在读取:

/sqrt+

树现在是:

      +
    /    sqrt
  +   .    .
.   .

接下来,第三个CCD_ 22成为CCD_。因此,队列将是:

/sqrt++

树将是(对不起,我的ASCII艺术很弱):

      +
     /    sqrt
  +    +    .
 . .  . .   

现在满足了/,所以当您点击e时,您将从队列中弹出/。现在sqrt是队列的开始,所以e被附加到它。队列现在是:

sqrt++

树是:

      +
     /    sqrt
  +    +    e
 . .  . .

接下来的四次迭代显然将a、b、c、d分配给剩余的叶子,从而得到解析树。

顺便说一句,std::dequeue是用于队列的完美数据结构。

只需构建一个树T。每个节点都是一个元组(terminal,)(unary_operator, operand)(binary_operator, first_operand, second_operand)。操作数本身为树中节点的索引。

例如,表达式a + (b / c)将具有树T[0] = (+, 1, 2), T[1] = (a,), T[2] = (/, 3, 4), T[3] = (b,), T[4] = (c,)。一旦你有了这个,就做一个预购。以下是用于此操作的Python代码。

def preorder(T, i):
  X = [T[i][0]]
  if len(T[i]) > 1:
    X.extend(preorder(T, T[i][1]))
  if len(T[i]) > 2:
    X.extend(preorder(T, T[i][2]))
  return X
def convert(A):
  binary_operators = ['+', '-', '/']
  unary_operators = ['sqrt']
  left = 0
  right = 0
  T = dict([(i, ()) for i in range(len(A))])
  for a in A:
    if a in binary_operators:
      T[left] = (a, right + 1, right + 2)
      right += 2
    elif a in unary_operators:
      T[left] = (a, right + 1)
      right += 1
    else:
      T[left] = (a,)
    left += 1
  return preorder(T, 0)
def main(argv=None):
  A = ['+', '/', 'sqrt', '+', '+', 'e', 'a', 'b', 'c', 'd']
  print convert(A)

当您从ORF构建T时,保留一个左右指针,该指针告诉您必须在表达式树中填写的第一个节点,而right则告诉您最后一个节点。