我需要一个算法来使用可选和替代节点来遍历树以计算所有可能的路径

时间:2018-04-30 12:52:16

标签: algorithm tree-traversal

鉴于以下树是一种Backus-Nauf形式,如符号所示 |表示或(所以B是F或G)和[]表示可选(因此H是可选的)

Def: A B C
A: D E
B: F | G
C: [H] I
D: a b
E: c d
F: e f
G: g h
H: i j
I: k l

a: a
b: b
c: c
d: d
e: e
f: f
g: g
h: h
i: i
j: j
k: k
l: l    

可以视为

              Def
    A          B          C
 D     E    F  |  G   [H]    I
a b   c d  e f   g h  i j   k l

我需要走树提取叶节点并转换为下面的树,它提供了可能的路径

 Def
     a
         b
             c
                 d
                     e
                         f
                             i
                                 j
                                     k
                                         l
                             k
                                 l
                     g
                         h
                             i
                                 j
                                     k
                                         l
                             k
                                 l

所以可能的路径是

abcdefijkl

abcdefkl

abcdghijkl

abcdghkl

我的repo有一个失败的C#单元测试(设置树并调用一个基本的重复漫步者),这应该有希望澄清我想要实现的目标。

我无法弄清楚如何在可选节点和替代节点上进行分支,同时保持正确的叶子以附加后续叶子。

2 个答案:

答案 0 :(得分:1)

非递归广度优先搜索可能会沿着这些行看(伪代码)。通过致电findAllLeafPaths(Def)来启动:

var allPathsFound = {}
function findAllLeafPaths(startNode) {
    var tokenSequenceQueue = {
        createTokenSequenceFrom(startNode)
    }
    while (tokenSequenceQueue.isNotEmpty()) {
        var tokenSequence = tokenSequenceQueue.removeFirst()
        var allLeaves = true
        for (every token T in tokenSequence) {
            if isLeafNode(T)
                continue
            else if T's rule is "T: Y Z" {
                allLeaves = false
                tokenSequenceQueue.append(tokenSequence.replace(T, Y + Z))
            } else if T's rule is "T: [Y] Z" {
                allLeaves = false
                tokenSequenceQueue.append(tokenSequence.replace(T, Y + Z))
                tokenSequenceQueue.append(tokenSequence.replace(T, Z))
            } else if T's rule "T: Y | Z" {
                allLeaves = false
                tokenSequenceQueue.append(tokenSequence.replace(T, Y))
                tokenSequenceQueue.append(tokenSequence.replace(T, Z))
            }
        }
        if (allLeaves) {
            allPathsFound.add(tokenSequence)
        }
    }
}

这里也是一个递归深度优先版本。我更喜欢第一个,因为递归使您的堆栈受到结果路径的最大可能长度的支配:

var allPathsFound = {}
function toLeafNodes(tokenSequence) {
    var allLeaves = true
    for every token T in tokenSequence {
        if isLeafNode(T)
            continue
        else if T's rule is "T: Y Z" {
            allLeaves = false
            toLeafNodes(tokenSequence.replace(T, Y + Z)
        } else if T's rule is "T: [Y] Z" {
            allLeaves = false
            toLeafNode(tokenSequence.replace(T, Y + Z)
            toLeafNode(tokenSequence.replace(T, Z)
        } else if T's rule "T: Y | Z" {
            allLeaves = false
            toLeafNode(tokenSequence.replace(T, Y)
            toLeafNode(tokenSequence.replace(T, Z)
        }
    }
    if (allLeaves) {
        allPathsFound.add(tokenSequence)
    }
}

[编辑]非递归版本目前一次替换一个,并立即将结果序列放在队列中。它可以进一步优化,一次性完成所有可能的替换。

答案 1 :(得分:0)

还有另一种方法可以根据您的定义构建树。考虑:

void printStuff(int x){

    while(true){    
        std::cout <<" Hi from thread 1 in group " << x << std::endl;        
        boost::this_thread::sleep( boost::posix_time::milliseconds(1000) );
     }
}

void pstwo(int x){
    while(true){    
        std::cout <<" Hi from thread 2 in group " << x << std::endl;    
        boost::this_thread::sleep( boost::posix_time::milliseconds(1500) );
     }
 }

int main()
{
    boost::asio::io_service io_service;

    boost::asio::io_service::work work(io_service);

    boost::asio::io_service io_service2;

    boost::asio::io_service::work work2(io_service2);

    boost::thread_group threads;
    boost::thread_group threadsTwo;
    for (std::size_t i = 0; i < 2; ++i)
      threads.create_thread(boost::bind(&boost::asio::io_service::run, &io_service));

    for (std::size_t i = 0; i < 2; ++i)
      threadsTwo.create_thread(boost::bind(&boost::asio::io_service::run, &io_service2));

    io_service.post(boost::bind(printStuff,1));
    io_service.post(boost::bind(pstwo,1));

    io_service2.post(boost::bind(printStuff,2));
    io_service2.post(boost::bind(pstwo,2));

    boost::this_thread::sleep( boost::posix_time::milliseconds(10000));
    io_service.stop();
    std::cout<<"------------------"<<std::endl;
    boost::this_thread::sleep( boost::posix_time::milliseconds(10000));
    io_service.run();    
    io_service2.stop();
    std::cout<<"-----------------"<<std::endl;
    boost::this_thread::sleep( boost::posix_time::milliseconds(10000));
    io_service.stop();
    threads.join_all();
    threadsTwo.join_all();
    return 0;
}

开始
Def: A B C
A: D E
B: F | G
C: [H] I

然后用D E替换A:

A
 \
  B
   \
    C

使用B(替换为F | G)和C(替换为[H] I)执行相同的操作,然后得到:

D
 \
  E
   \
    B
     \
      C

现在,如果您对该树执行递归深度优先遍历,则会获得有效字符串:

      D
       \
        E     
    /       \
   F         G
  / \       / \
 I   H     I   H
      \         \
       I         I

当您输出字符串时,您可以用&#34; b&#34;等替换D.

我一步一步地展示了它,但你可以一次性完成所有这些。