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