减少通用树遍历中的分支

Posted

技术标签:

【中文标题】减少通用树遍历中的分支【英文标题】:Reducing Branching in Generic Tree Traversal 【发布时间】:2016-10-29 05:43:38 【问题描述】:

所以我不完全确定这个问题是属于这里还是属于软件工程,但由于我对 C++ 实现特别感兴趣,所以我在这里问。

现在,除此之外,我已经实现了一个基本的二叉树,它带有一个通用的遍历函数,旨在同时处理前序、中序和后序。

void BiTree::_trav(BiNode* root, int offset, function<void (int&,int&)> lambda) 

    if (root == NULL) return;
    auto call = [&] () lambda(root->data, root->sc);;

    if (offset == 0)  call();

    _trav(root->child[0], offset, lambda);

    if (offset == 1)  call();

    _trav(root->child[1], offset, lambda);

    if (offset == 2)  call();

如你所见,这个函数递归调用自身,并且有很多分支。由于不是每个人都知道 C++ 或 C++ 中的 lambdas(我昨天不知道),因此将相同的想法归结为问题。

void BiTree::_trav(BiNode* root, int offset) 

    if (root == NULL) return;

    if (offset == 0)  do_thing();

    _trav(root->child[0], offset, lambda);

    if (offset == 1)  do_thing();

    _trav(root->child[1], offset, lambda);

    if (offset == 2)  do_thing();

我的编程直觉说有一种更好的方法可以做到这一点,分支更少,但我不知道如何。 switch 语句不起作用,因为do-thing() 被多次调用,或者有更多分支。所以我对如何用最少的分支进行通用树遍历很感兴趣。

【问题讨论】:

一体化和最佳性能往往是相互矛盾的目标。鉴于每个函数只有 5 行代码,通过编写三个独立的函数来实现最大性能:前序、中序和后序。它不仅消除了分支,而且还消除了在每次调用时传递offset 的需要。 【参考方案1】:

所以我正在使用一组虚拟函数来回答我自己的问题,并将该函数与我实际想要运行的代码插入到我想要运行它的位置。我不确定这是否会提高性能,但似乎减少了分支。

void BiTree::_trav(BiNode* root, int offset, function<void (int&,int&)> lambda) 
    if (root == NULL) return;

    function<void (void)> branches[3];
    for(int i = 0; i < 3; i++) 
        branches[i] = [&] () ;
    
    function<void (void)> call  = [&] () lambda(root->data, root->sc);;
    branches[offset] = call;

    branches[0]();

    _trav(root->child[0], offset, lambda);

    branches[1]();

    _trav(root->child[1], offset, lambda);

    branches[2]();

【讨论】:

以上是关于减少通用树遍历中的分支的主要内容,如果未能解决你的问题,请参考以下文章

二分搜索树的深度优先遍历和广度优先遍历

二叉树的层次遍历

通过避免特定分支快速遍历lxml树

遍历树收集所有子节点,不包括不相关的分支

JS中的二叉树遍历

我正在尝试对二叉树进行曲折遍历。它没有打印最后一个分支的 5 个,为啥?