C++ 将条件递归算法转化为迭代算法

C++ 将条件递归算法转化为迭代算法,c++,algorithm,recursion,tree,C++,Algorithm,Recursion,Tree,最近,我编写了一个基于递归的算法来水平打印二叉树。 一般来说,我在将基于递归的算法转换为基于迭代的算法时没有任何问题,但我就是不知道如何做到这一点 假设我们是一个向量 std::vector<int> tree = {10,9,8,7,6,5,4}; 我的算法通过以下途径工作: index -> left -> left Or in our case 10 -> 9 -> 7 -> right

最近,我编写了一个基于递归的算法来水平打印二叉树。 一般来说,我在将基于递归的算法转换为基于迭代的算法时没有任何问题,但我就是不知道如何做到这一点

假设我们是一个向量

std::vector<int> tree = {10,9,8,7,6,5,4};
我的算法通过以下途径工作:

index ->  left -> left      Or in our case 10 -> 9 -> 7
               -> right                            -> 6
      -> right -> left                        -> 8 -> 5
               -> right                            -> 4
等等,并根据树的大小进行扩展。当我有条件地使用递归时,我不知道如何将代码转换为while循环。我不太擅长解释,所以这里是代码

#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <unistd.h>

/* The Padding function handles the spacing and the vertical lines whenever we print a  *
 * right child. This depends on the previous parent-child hierarchy of the child        *
 * Printer handles the work of printing the elements and uses a depth-first-search      *
 * algorithm as it's core. Left children are printed horizontally while the right ones  *   
 * are printed vertically. Print_tree is a wrapper. It also finds the max-length value  *
 * which will be used for formatting so that the formatting won't get messed up because *
 * of the different number of digits.                                                   */           

std::string do_padding (unsigned index, unsigned mlength){
  std::string padding;
  while(int((index-1)/2) != 0){
    padding = (int((index-1)/2) % 2 == 0) ?
    std::string(mlength+4,' ') + " "  + padding :
    std::string(mlength+3,' ') + "| " + padding ;
    index = int((index-1)/2);
  }
  return padding;
}

template <class T>
void printer (std::vector<T> const & tree, unsigned index, unsigned mlength){
  auto last = tree.size() - 1 ;
  auto  left = 2 * index + 1 ;
  auto  right = 2 * index + 2 ;
  std::cout << " " << tree[index] << " " ;
  if (left <= last){
    auto llength = std::to_string(tree[left]).size();
    std::cout << "---" << std::string(mlength - llength,'-');
    printer(tree,left,mlength);
    if (right <= last) {
      auto rlength = std::to_string(tree[right]).size();
      std::cout << std::endl<< do_padding(right,mlength) << std::string(mlength+ 3,' ') << "| " ;
      std::cout << std::endl << do_padding(right,mlength) << std::string(mlength+ 3,' ') << "└─" <<
      std::string(mlength - rlength,'-');
      printer(tree,right,mlength);
    }
  }
}

template <class T>
void print_tree (std::vector<T> & tree){
  unsigned mlength = 0;
  for (T & element : tree){
    auto length = std::to_string(element).size();
    if (length > mlength) {
      mlength = length;
    }
  }
  std::cout <<  std::fixed << std::string(mlength- std::to_string(tree[0]).size(),' ') ;
  printer(tree,0,mlength);
}

int main() {
  std::vector<int> test;
  for (auto i =0; i != 200; ++i) {
    test.push_back(rand() % 12200);
  }
  std::make_heap(test.begin(),test.end());
  std::cout << std::endl << "Press ENTER to show heap tree.." << std::endl;
  std::cin.ignore();
  print_tree(test);
  std::cout << std::endl;
}
#包括
#包括
#包括
#包括
#包括
/*每当我们打印一个文件时,Padding函数处理间距和垂直线*
*对孩子。这取决于子对象的上一个父子层次结构*
*打印机处理打印元素的工作,并使用深度优先搜索*
*算法作为它的核心。左侧子项水平打印,而右侧子项*
*它们是垂直打印的。打印树是一个包装器。它还查找“最大长度”值*
*它将用于格式化,这样格式就不会因为*
*数字的不同位数。*/
字符串填充(无符号索引,无符号长度){
字符串填充;
而(int((索引-1)/2)!=0){
填充=(int((索引-1)/2)%2==0)?
标准::字符串(最大长度+4')+“”+填充:
字符串(最大长度+3',)+“|”+填充;
指数=int((指数-1)/2);
}
返回填充;
}
模板
无效打印机(标准::向量常量和树、无符号索引、无符号最大长度){
auto last=tree.size()-1;
自动左=2*索引+1;
自动右键=2*索引+2;

std::cout我记得,你发布了这个问题。因此,对我来说(没有阅读代码),你的树遍历的核心算法是一个

要使此递归算法迭代,您可以使用一个堆栈。。该堆栈基本上包含所有访问的节点,并允许返回它所采用的路径。上给出了一个示例。

对于大多数遍历树的算法,如果您希望在非递归中对它们进行公式化,则需要额外的数据结构对于深度优先遍历(如您的情况),通常使用堆栈(对于宽度优先遍历,使用队列…)

在伪代码中,打印树看起来像

function print_tree(tree_vector)
  s = new stack<int>()
  s.push(0)
  while (!s.empty())
    index = s.pop()
    print (tree_vector[index])
    left = 2*index + 1
    right = 2*index + 2
    if (right < tree_vector.size())
      s.push(right)
    if (left < tree_vector.size())
      s.push(left)
  end while
end function
函数打印树(树向量)
s=新堆栈()
s、 推送(0)
而(!s.empty())
索引=s.pop()
打印(树向量[索引])
左=2*索引+1
右=2*索引+2
if(右

请注意,此堆栈隐式表示编译器在进行递归调用时在内部使用的调用堆栈。

这似乎正是我所要寻找的。由于我们将不得不使用堆栈,我想知道它是否值得。这感觉好像剥夺了使用迭代方法的优势。“[…]使用迭代方法的优点。”你的意思是递归吗?不管怎样:优点和缺点在链接的问题中有描述。我发现迭代的优点是它不使用系统堆栈。如果我不得不自己使用堆栈,并且失去可读性,那么使用迭代方法似乎没有多大意义。我在理论方面不是很好,我很好很多都是自学成才的,所以可能我弄错了。我认为关键的一点是要让堆栈上的项目顺序正确(反过来!).就个人而言,我更喜欢迭代法,b/c我认为它可能更容易调试,而且它可以移植到VHDL,但这可能不重要;)谢谢你的帮助!非常感谢。非常清楚的答案,它也解决了我对Sebastian答案的担忧。令人惊讶的是,在可读性方面没有太大损失。顺便说一句,我不确定,因为我这是我第一次看到这个算法,但也许它应该先检查右,然后检查左?你是对的:为了得到与原始算法相同的行为,条件检查的顺序应该交换。。。
function print_tree(tree_vector)
  s = new stack<int>()
  s.push(0)
  while (!s.empty())
    index = s.pop()
    print (tree_vector[index])
    left = 2*index + 1
    right = 2*index + 2
    if (right < tree_vector.size())
      s.push(right)
    if (left < tree_vector.size())
      s.push(left)
  end while
end function