Recursion 易递归算法的迭代版本

Recursion 易递归算法的迭代版本,recursion,iteration,Recursion,Iteration,我想我有一个很简单的问题。 我有一个问题,可以用递归函数很容易地解决,但我不能迭代地解决 假设有任意布尔矩阵,如: M: 我知道这不是一个普通的布尔矩阵,但它对我的例子很有用。 你可以注意到里面有一些零路径 我想做一个函数,它接收这个矩阵和一个存储零的点,并将同一区域中的每个零转换为2(假设矩阵可以存储任何整数,即使它最初是布尔的) (就像在paint或任何图像编辑器中绘制分区一样) 假设我用这个矩阵M和右上角零的坐标调用函数,结果是: 111011111112 110111111122 001

我想我有一个很简单的问题。 我有一个问题,可以用递归函数很容易地解决,但我不能迭代地解决

假设有任意布尔矩阵,如:

M:

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

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

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

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

111011111112
110111111122
001111111121
100111111121
110011111221
111111112211
111111122111
111112221111
好吧,我的问题是如何迭代地做这个。。。 希望我没有把事情搞得太糟

提前谢谢

曼努埃尔

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

伪代码:

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)

为此,我将使用堆栈ou队列对象。这是我的伪代码(类似python):


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

伪代码:

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*/ )

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

此代码的递归版本如下(伪代码-无边界检查):

这不是简单的尾部递归(不止一个递归调用),因此必须添加某种堆栈结构来处理中间内存。一个版本如下所示(伪代码,java风格,同样,无边界检查):


并非所有的递归算法都可以转化为迭代算法。通常只有具有单个分支的线性算法才可以。这意味着有两个或更多分支的树算法和有更多路径的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)
部分迭代(try):

treesum:Tree->N
树丛(t)==
res=0
而tnil
let(左、节点、右)=t in
res=res+node+treesum(右)
t=左
返回res
如您所见,有两条路径(左和右)。可以将其中一条路径转换为迭代,但要将另一条路径转换为迭代,您需要保留可以使用堆栈完成的状态:

迭代(使用堆栈):

treesum:Tree->N
树丛(t)==
res=0
堆栈推送(t)
而不是stack.isempty()
t=stack.pop()
而tnil
let(左、节点、右)=t in
stack.pop(右)
res=res+node+treesum(右)
t=左
返回res

这是可行的,但递归算法更容易理解。

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

  • 将首字母设置为2
  • 扫描矩阵以查找2附近的0
  • 如果找到这样的0,请将其更改为2,并在步骤2中重新启动扫描

  • 这很容易理解,不需要堆栈,但非常耗时。

    一种简单的迭代方法是使用队列

  • 将起始点插入队列
  • 从队列中获取第一个元素
  • 设置为2
  • 将仍为0的所有邻居放入队列
  • 如果队列不是空的,跳到2

  • 使用堆栈/队列对象仍然是递归的,您自己管理堆栈,而不是使用编程语言来为您进行管理。False。递归是指调用或引用自身的函数。我同意你的观点,编译后的代码在功能上是一样的,这不是严格的递归。一个理由是非递归版本可以在单过程解析器中解析。@尼克:在函数式编程中,递归过程使用非常量空间进行延迟操作,而迭代过程使用常量空间。因此,使用堆栈或队列是一个递归过程()。请参阅ascobol文章的注释-从技术上讲,这仍然是递归的。您使用的“递归”定义与其他人不同。每个递归都可以转换为交互——请参阅
    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));              
           }          
        }
    }
    
    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