Recursion 从递归到迭代的方法

Recursion 从递归到迭代的方法,recursion,iteration,language-agnostic,computer-science,theory,Recursion,Iteration,Language Agnostic,Computer Science,Theory,在我多年的编程中,我经常使用递归来解决简单的问题,但我完全知道,有时由于内存/速度问题,需要迭代 所以,在很久以前的某个时候,我试着去寻找是否存在任何“模式”或教科书式的方法来将一种常见的递归方法转换为迭代,但什么也没有发现。或者至少在我记忆中没有什么能帮上忙 有一般规则吗 有“模式”吗 努力进行递归调用(递归,其中最后一条语句是递归调用)。一旦有了它,将其转换为迭代通常是相当容易的。努力进行递归调用(递归,其中最后一条语句是递归调用)。一旦你有了它,将其转换为迭代通常是相当容易的。在谷歌搜索

在我多年的编程中,我经常使用递归来解决简单的问题,但我完全知道,有时由于内存/速度问题,需要迭代

所以,在很久以前的某个时候,我试着去寻找是否存在任何“模式”或教科书式的方法来将一种常见的递归方法转换为迭代,但什么也没有发现。或者至少在我记忆中没有什么能帮上忙

  • 有一般规则吗
  • 有“模式”吗

    • 努力进行递归调用(递归,其中最后一条语句是递归调用)。一旦有了它,将其转换为迭代通常是相当容易的。

      努力进行递归调用(递归,其中最后一条语句是递归调用)。一旦你有了它,将其转换为迭代通常是相当容易的。

      在谷歌搜索“延续传递样式”。有一个转换为尾部递归样式的一般过程;还有一个将尾部递归函数转换为循环的通用过程。

      在谷歌搜索“延续传递样式”。还有一个转换为尾部递归样式的通用过程;还有一个将尾部递归函数转换为循环的一般过程。

      要寻找的一种模式是在函数末尾调用递归(所谓的尾部递归)。这可以很容易地用一段时间来代替。例如,函数foo:

      void foo(Node* node)
      {
          if(node == NULL)
             return;
          // Do something with node...
          foo(node->left);
          foo(node->right);
      }
      
      以调用foo结束。这可以替换为:

      void foo(Node* node)
      {
          while(node != NULL)
          {
              // Do something with node...
              foo(node->left);
              node = node->right;
           }
      }
      

      这消除了第二个递归调用。

      要查找的一个模式是函数末尾的递归调用(所谓的尾部递归)。这可以很容易地用一段时间来代替。例如,函数foo:

      void foo(Node* node)
      {
          if(node == NULL)
             return;
          // Do something with node...
          foo(node->left);
          foo(node->right);
      }
      
      以调用foo结束。这可以替换为:

      void foo(Node* node)
      {
          while(node != NULL)
          {
              // Do something with node...
              foo(node->left);
              node = node->right;
           }
      }
      

      这消除了第二个递归调用。

      一般来说,递归可以通过使用存储变量模拟为迭代。注意递归和迭代通常是等价的;一个几乎总是可以转换成另一个。尾部递归函数很容易转换为迭代函数。只需将累加器变量设置为局部变量,并进行迭代而不是递归。下面是C++中的一个例子(C是不是使用默认参数):


      了解我,我可能在代码中犯了一个错误,但想法就在那里。

      一般来说,递归可以通过使用存储变量模拟为迭代。注意递归和迭代通常是等价的;一个几乎总是可以转换成另一个。尾部递归函数很容易转换为迭代函数。只需将累加器变量设置为局部变量,并进行迭代而不是递归。下面是C++中的一个例子(C是不是使用默认参数):


      了解我,我可能在代码中犯了一个错误,但想法就在那里。

      实际上,最常见的方法是保留自己的堆栈。下面是C中的递归快速排序函数:

      void quicksort(int* array, int left, int right)
      {
          if(left >= right)
              return;
      
          int index = partition(array, left, right);
          quicksort(array, left, index - 1);
          quicksort(array, index + 1, right);
      }
      
      下面是我们如何通过保持自己的堆栈使其迭代:

      void quicksort(int *array, int left, int right)
      {
          int stack[1024];
          int i=0;
      
          stack[i++] = left;
          stack[i++] = right;
      
          while (i > 0)
          {
              right = stack[--i];
              left = stack[--i];
      
              if (left >= right)
                   continue;
      
              int index = partition(array, left, right);
              stack[i++] = left;
              stack[i++] = index - 1;
              stack[i++] = index + 1;
              stack[i++] = right;
          }
      }
      

      显然,这个例子没有检查堆栈边界。。。实际上,您可以根据给定的左、右值来调整堆栈的大小。但是你明白了。

      实际上,最常用的方法是保留自己的堆栈。下面是C中的递归快速排序函数:

      void quicksort(int* array, int left, int right)
      {
          if(left >= right)
              return;
      
          int index = partition(array, left, right);
          quicksort(array, left, index - 1);
          quicksort(array, index + 1, right);
      }
      
      下面是我们如何通过保持自己的堆栈使其迭代:

      void quicksort(int *array, int left, int right)
      {
          int stack[1024];
          int i=0;
      
          stack[i++] = left;
          stack[i++] = right;
      
          while (i > 0)
          {
              right = stack[--i];
              left = stack[--i];
      
              if (left >= right)
                   continue;
      
              int index = partition(array, left, right);
              stack[i++] = left;
              stack[i++] = index - 1;
              stack[i++] = index + 1;
              stack[i++] = right;
          }
      }
      

      显然,这个例子没有检查堆栈边界。。。实际上,您可以根据给定的左、右值来调整堆栈的大小。但是你明白了。

      通常,我用迭代算法代替递归算法,将通常传递给递归函数的参数推到堆栈上。事实上,您正在用自己的程序堆栈替换程序堆栈

      var堆栈=[];
      stack.push(firstObject);
      //而不是空的
      while(堆栈长度){
      //从堆栈的末尾弹出。
      obj=stack.pop();
      //做事。
      //根据需要推送堆栈上的其他对象。
      ...
      }
      
      注意:如果内部有多个递归调用,并且希望保留调用顺序,则必须按相反顺序将它们添加到堆栈中:

      foo(first);
      foo(second);
      
      必须由

      stack.push(second);
      stack.push(first);
      

      编辑:这篇文章(或)详细介绍了这个主题。

      通常,我会将通常传递给递归函数的参数推到堆栈上,用迭代算法替换递归算法。事实上,您正在用自己的程序堆栈替换程序堆栈

      var堆栈=[];
      stack.push(firstObject);
      //而不是空的
      while(堆栈长度){
      //从堆栈的末尾弹出。
      obj=stack.pop();
      //做事。
      //根据需要推送堆栈上的其他对象。
      ...
      }
      
      注意:如果内部有多个递归调用,并且希望保留调用顺序,则必须按相反顺序将它们添加到堆栈中:

      foo(first);
      foo(second);
      
      必须由

      stack.push(second);
      stack.push(first);
      

      编辑:这篇文章(或)详细介绍了这个主题。

      即使使用堆栈也不会将递归算法转换为迭代算法。正常的递归是基于函数的递归,如果我们使用堆栈,那么它就变成了基于堆栈的递归。但它仍然是递归的

      对于递归算法,空间复杂度为O(N),时间复杂度为O(N)。 对于迭代算法,空间复杂度为O(1),时间复杂度为O(N)


      但是,如果我们使用堆栈,那么在复杂性方面的东西保持不变。我认为只有尾部递归可以转换为迭代。

      即使使用堆栈也不能将递归算法转换为迭代算法。正常的递归是基于函数的递归,如果我们使用堆栈,那么它就变成了基于堆栈的递归。但它仍然是递归的

      对于递归算法,空间复杂度为O(N),时间复杂度为O(N)。 对于迭代算法,空间复杂度为O(1),时间复杂度为O(N)

      但是如果我们
      int Sum(int[] ar)
      {
       return RecursionHelper<int>.CreateSingular(i => i >= ar.Length, i => 0)
       .RecursiveCall((i, rv) => i + 1)
       .Do((i, rv) => ar[i] + rv)
       .Execute(0);
      }
      
      struct tnode
      {
          tnode(int n) : data(n), left(0), right(0) {}
          tnode *left, *right;
          int data;
      };
      
      void insertnode_recur(tnode *node, int num)
      {
          if(node->data <= num)
          {
              if(node->right == NULL)
                  node->right = new tnode(num);
              else
                  insertnode(node->right, num);
          }
          else
          {
              if(node->left == NULL)
                  node->left = new tnode(num);
              else
                  insertnode(node->left, num);
          }    
      }
      
      // Identify the stack variables that need to be preserved across stack 
      // invocations, that is, across iterations and wrap them in an object
      struct stackitem 
      { 
          stackitem(tnode *t, int n) : node(t), num(n), ra(0) {}
          tnode *node; int num;
          int ra; //to point of return
      };
      
      void insertnode_iter(tnode *node, int num) 
      {
          vector<stackitem> v;
          //pushing a stackitem is equivalent to making a recursive call.
          v.push_back(stackitem(node, num));
      
          while(v.size()) 
          {
              // taking a modifiable reference to the stack item makes prepending 
              // 'si.' to auto variables in recursive logic suffice
              // e.g., instead of num, replace with si.num.
              stackitem &si = v.back(); 
              switch(si.ra)
              {
              // this jump simulates resuming execution after return from recursive 
              // call 
                  case 1: goto ra1;
                  case 2: goto ra2;
                  default: break;
              } 
      
              if(si.node->data <= si.num)
              {
                  if(si.node->right == NULL)
                      si.node->right = new tnode(si.num);
                  else
                  {
                      // replace a recursive call with below statements
                      // (a) save return point, 
                      // (b) push stack item with new stackitem, 
                      // (c) continue statement to make loop pick up and start 
                      //    processing new stack item, 
                      // (d) a return point label
                      // (e) optional semi-colon, if resume point is an end 
                      // of a block.
      
                      si.ra=1;
                      v.push_back(stackitem(si.node->right, si.num));
                      continue; 
      ra1:            ;         
                  }
              }
              else
              {
                  if(si.node->left == NULL)
                      si.node->left = new tnode(si.num);
                  else
                  {
                      si.ra=2;                
                      v.push_back(stackitem(si.node->left, si.num));
                      continue;
      ra2:            ;
                  }
              }
      
              v.pop_back();
          }
      }
      
      void insertnode_iter(tnode *node, int num) 
      {
      
          vector<stackitem> v;
          v.push_back(stackitem(node, num));
      
          while(v.size())
          {
              stackitem &si = v.back(); 
              switch(si.ra)
              {
                  case 1: goto ra1;
                  case 2: goto ra2;
                  default: break;
              } 
      
              if(si.node->data <= si.num)
              {
                  if(si.node->right == NULL)
                      si.node->right = new tnode(si.num);
                  else
                  {
      
                      si.ra=1;
                      v.push_back(stackitem(si.node->right, si.num));
                      continue; 
      ra1:            ;    
      
                  }
              }
              else
              {
                  if(si.node->left == NULL)
                      si.node->left = new tnode(si.num);
                  else
                  {
      
                      si.ra=2;                
                      v.push_back(stackitem(si.node->left, si.num));
                      continue;
      ra2:            ;
      
                  }
              }
      
              v.pop_back();
          }
      
      }
      
      function show(node)
      0. if isleaf(node):
      1.  print node.name
      2. else:
      3.  show(node.left)
      4.  show(node)
      5.  show(node.right)
      
      function rec(...) {
        for/while loop {
          var x = rec(...)
          // make a side effect involving return value x
        }
      }
      
      typedef struct {
          int32_t type;
          int32_t valueint;
          double  valuedouble;
          struct  cNODE *next;
          struct  cNODE *prev;
          struct  cNODE *child;
      } cNODE;
      
      void cNODE_Delete(cNODE *c) {
          cNODE*next;
          while (c) {
              next=c->next;
              if (c->child) { 
                cNODE_Delete(c->child)
              }
              free(c);
              c=next;
          }
      }
      
      void cNODE_Delete (cNODE *c) {
          cNODE *tmp, *last = c;
          while (c) {
              while (last->next) {
                  last = last->next;   /* find last */
              }
              if ((tmp = c->child)) {
                  c->child = NULL;     /* append child to last */
                  last->next = tmp;
                  tmp->prev = last;
              }
              tmp = c->next;           /* remove current */
              free(c);
              c = tmp;
          }
      }
      
      if(task can be done directly) {
          return result of doing task directly
      } else {
          split task into two or more parts
          solve for each part (possibly by recursing)
          return result constructed by combining these solutions
      }
      
      if(the number of discs to move is 1) {
          just move it
      } else {
          move n-1 discs to the spare peg
          move the remaining disc to the target peg
          move n-1 discs from the spare peg to the target peg, using the current peg as a spare
      }
      
      place seed task on stack
      while stack is not empty 
         take a task off the stack
         if(task can be done directly) {
            Do it
         } else {
            Split task into two or more parts
            Place task to consolidate results on stack
            Place each task on stack
         }
      }
      
      stack.push(new Task(size, from, to, spare));
      while(! stack.isEmpty()) {
          task = stack.pop();
          if(task.size() = 1) {
              just move it
          } else {
              stack.push(new Task(task.size() -1, task.spare(), task,to(), task,from()));
              stack.push(new Task(1, task.from(), task.to(), task.spare()));
              stack.push(new Task(task.size() -1, task.from(), task.spare(), task.to()));
          }
      }
      
      #include <iostream>
      #include <stack>
      using namespace std;
      
      int GCD(int a, int b) { return b == 0 ? a : GCD(b, a % b); }
      
      struct Par
      {
          int a, b;
          Par() : Par(0, 0) {}
          Par(int _a, int _b) : a(_a), b(_b) {}
      };
      
      int GCDIter(int a, int b)
      {
          stack<Par> rcstack;
      
          if (b == 0)
              return a;
          rcstack.push(Par(b, a % b));
      
          Par p;
          while (!rcstack.empty()) 
          {
              p = rcstack.top();
              rcstack.pop();
              if (p.b == 0)
                  continue;
              rcstack.push(Par(p.b, p.a % p.b));
          }
      
          return p.a;
      }
      
      int main()
      {
          //cout << GCD(24, 36) << endl;
          cout << GCDIter(81, 36) << endl;
      
          cin.get();
          return 0;
      }
      
      (defn factorial [n]
        (if (< n 2)
          1
          (*' n (factorial (dec n)))))
      
      (defn factorial [n]
        (loop [n n
               stack []]
          (if (< n 2)
            (return 1 stack)
            ;; else loop with new values
            (recur (dec n)
                   ;; push function onto stack
                   (cons (fn [n-1!]
                           (*' n n-1!))
                         stack)))))
      
      (defn return
        [v stack]
        (reduce (fn [acc f]
                  (f acc))
                v
                stack))
      
      (defn ackermann [m n]
        (cond
          (zero? m)
          (inc n)
      
          (zero? n)
          (recur (dec m) 1)
      
          :else
          (recur (dec m)
                 (ackermann m (dec n)))))
      
      (defn ackermann [m n]
        (loop [m m
               n n
               stack []]
          (cond
            (zero? m)
            (return (inc n) stack)
      
            (zero? n)
            (recur (dec m) 1 stack)
      
            :else
            (recur m
                   (dec n)
                   (cons #(ackermann (dec m) %)
                         stack)))))