易递归算法的迭代版本

时间:2009-01-19 08:57:58

标签: recursion iteration

我认为我有一个非常简单的问题。 我有这个问题,可以通过递归函数很容易地解决,但是我无法迭代地解决。

假设您有任何布尔矩阵,例如:

L:

111011111110
110111111100
001111111101
100111111101
110011111001
111111110011
111111100111
111110001111

我知道这不是一个普通的布尔矩阵,但它对我的例子很有用。 你可以注意到那里有一些零路径......

我想创建一个接收此矩阵的函数和一个存储零的点,并将同一区域中的每个零转换为2(假设矩阵可以存储任何整数,即使它最初是布尔值)

(就像在Paint或任何图像编辑器中绘制区域一样)

假设我用这个矩阵M调用函数并且右上角的坐标为零,结果将是:

111011111112
110111111122
001111111121
100111111121
110011111221
111111112211
111111122111
111112221111

好吧,我的问题是如何迭代地执行此操作... 希望我没有把它弄得太乱了

提前致谢!

曼努埃尔

ps:如果您能用C,S,python或伪代码显示函数,我将不胜感激,请:D

7 个答案:

答案 0 :(得分:6)

有一种标准技术可以将特定类型的递归算法转换为迭代算法。它被称为tail-recursion

此代码的递归版本看起来像(伪代码 - 没有边界检查):

paint(cells, i, j) {
   if(cells[i][j] == 0) {
      cells[i][j] = 2;
      paint(cells, i+1, j);
      paint(cells, i-1, j);
      paint(cells, i, j+1);
      paint(cells, i, j-1);
   }
}

这不是简单的尾递归(多个递归调用),因此您必须添加某种堆栈结构来处理中间内存。一个版本看起来像这样(伪代码,java-esque,再次,没有边界检查):

paint(cells, i, j) {
    Stack todo = new Stack();
    todo.push((i,j))
    while(!todo.isEmpty()) {
       (r, c) = todo.pop();
       if(cells[r][c] == 0) {
          cells[r][c] = 2;
          todo.push((r+1, c));
          todo.push((r-1, c));
          todo.push((r, c+1));
          todo.push((r, c-1));              
       }          
    }
}

答案 1 :(得分:1)

的伪代码:

Input: Startpoint (x,y), Array[w][h], Fillcolor f

Array[x][y] = f
bool hasChanged = false;
repeat
  for every Array[x][y] with value f:
    check if the surrounding pixels are 0, if so:
      Change them from 0 to f
      hasChanged = true
until (not hasChanged)

答案 2 :(得分:1)

为此,我将使用Stack ou Queue对象。这是我的伪代码(python-like):

stack.push(p0)
while stack.size() > 0:
    p = stack.pop()
    matrix[p] = 2
    for each point in Arround(p):
       if matrix[point]==0:
          stack.push(point)

答案 3 :(得分:1)

将递归函数转换为迭代函数的最简单方法是利用堆栈数据结构来存储数据,而不是通过递归调用将其存储在调用堆栈上。

伪代码:

var s = new Stack();

s.Push( /*upper right point*/ );

while not s.Empty:

    var p = s.Pop()        
    m[ p.x ][ p.y ] = 2

    s.Push ( /*all surrounding 0 pixels*/ )

答案 4 :(得分:1)

并非所有递归算法都可以转换为迭代算法。通常只有单个分支的线性算法才可以。这意味着具有两个或更多分支的树算法和具有更多路径的2d算法极难在不使用堆栈的情况下传递到递归(这基本上是作弊)。

示例:

递归:

listsum: N* -> N
listsum(n) ==
  if n=[] then 0 
          else hd n + listsum(tl n)

迭代:

listsum: N* -> N
listsum(n) ==
  res = 0;
  forall i in n do
    res = res + i
  return res

递归:

treesum: Tree -> N
treesum(t) ==
  if t=nil then 0
  else let (left, node, right) = t in
    treesum(left) + node + treesum(right)

部分迭代(尝试):

treesum: Tree -> N
treesum(t) ==
  res = 0
  while t<>nil 
    let (left, node, right) = t in
      res = res + node + treesum(right)
      t = left
  return res

如您所见,有两条路径(左侧和右侧)。可以将其中一个路径转换为迭代,但要将另一个转换为迭代,您需要保留可以使用堆栈完成的状态:

迭代(带堆栈):

treesum: Tree -> N
treesum(t) ==
  res = 0

  stack.push(t)
  while not stack.isempty()
    t = stack.pop()
    while t<>nil 
      let (left, node, right) = t in
        stack.pop(right)
        res = res + node + treesum(right)
        t = left

  return res

这很有效,但递归算法更容易理解。

答案 5 :(得分:0)

如果迭代地执行它比性能更重要,我将使用以下算法:

  1. 设置初始2
  2. 扫描矩阵以找到接近2
  3. 的0
  4. 如果找到这样的0,请将其更改为2并在步骤2中重新开始扫描。
  5. 这很容易理解,不需要堆叠,但非常耗时。

答案 6 :(得分:0)

迭代执行此操作的一种简单方法是使用队列。

  1. 将起点插入队列
  2. 从队列中获取第一个元素
  3. 设为2
  4. 将所有仍为0的邻居放入队列
  5. 如果队列不为空,请跳转到2。