•
3 min read

递归

Archive · written in 2008
Table of Contents

若一个对象部分地包含自己,或用自己给自己定义,则这个对象是递归的;若一个过程调用自己,则这个过程也是递归的。

概念

以下3种情况,常常会用到递归。

定义是递归的

如阶乘、幂函数、裴波那契等。

long Factorial(long n) {
	if (n == 0) {
		return 1;
	} else {
		return n * Factorial(n - 1);
	}
}

数据结构是递归的

链表就是一种递归的数据结构。而树是用多重链表实现的,所以也是递归的结构。所以链表和树的一些算法,可以用递归过程来实现。

例如,搜索单链表最后一个节点的递归解法:

LinkNode *FindRear(LinkNode *f) {
	if (f == NULL) {
		return NULL
	} else if (f->link == NULL) {
		return f;
	} else { 
		return FindRear(f->link);
	}
};

单链表搜索给定值的节点:

void Search(LinkNode *f, T& x) {
	if (f == NULL) {
		return NULL;
	} else if (f->data == x) {
		return f;
	} else {
		Search(f->link, x)
	}
};

问题的解法是递归的

典型的就是汉诺塔。

#include <iostream.h>
#include <string.h>
void Hanoi(int n, String A, String B, String C) {
	if (n == 1) {
		cout << "Move top disk from peg " << A << " to peg " << C << endl;
	} else {
		Hanoi(n - 1, A, C, B);
		cout << "Move top disk from peg " << A << " to peg " << C << endl;
		Hanoi(n - 1, B, A, C);
	}
};

递归过程与递归工作栈

递归工作栈

每一层递归调用所需保存的信息构成一个工作记录。通常包括: 1.返回地址 2.在本次过程调用时,为与形参结合的实参创建副本 3.本层的局部变量值

每进入一层递归,系统就要建立一个新的工作记录,把上述项目登入,加到递归工作栈的栈顶。每退出一层递归,就从递归工作栈退出一个工作记录。

用栈实现递归过程的非递归算法

对于递归过程,可以利用栈将它改为非递归过程。以求裴波那契数列为例:

struct Node {
	long n;
	int tag;
};

long Fibnacci(long n) {
	Stack<Node> S;
	Node *w;
	long sum = 0;
	do {
		while (n > 1) {
			w->n = n;
			w->tag = 1;
			S.Push(w);
			n--;
		}
		sum = sum + n;
		while (S.IsEmpty() == false) {
			S.Pop(w);
			if (w->tag == 1) {
				w->tag = 2;
				S.Push(w);
				n = w->n - 2;
				break;
			}
		}
	} while (S.IsEmpty() == false);
	return sum;
};

用迭代法实现递归过程

一般对尾递归或单向递归的情形,都可以利用迭代的方法,将递归过程改为非递归过程。

如计算斐波那契数列的非递归算法:

long FibIter(long n) {
	if (n <= 1) {
		return n;
	}
	long twoback = 0, oneback = 1, Current;
	for (int i = 2; i <= n; i ++) {
		Current = twoback + oneback;
		twoback = oneback;
		oneback = Current;
	}
	return Current;
}

回溯法

用回溯法求解问题时常常使用递归方法进行试探,或使用栈帮助向前试探和回溯。以迷宫问题为例。

前进方向表的定义。

struct offsets {
	int a, b;
	char *dir;
};
offsets move[8];

递归解法。

int SeekPath(int x, int y) {
	int i, g, h;
	char *d;
	if (x == m && y == p) {
		return 1;
	}
	for (i = 0; i < 8; i ++) {
		g = x + move[i].a;
		h = y + move[i].b;
		d = move[i].dir;
		if (Maze[g][h] == 0 &R& mark[g][h] == 0) {
			mark[g][h] = 1;
			if (SeekPath(g, h)) {
				cout << "(" << g << "," << h << "),"
				    << "Direction" << dir << ",";
				return 1;
			}
		}
	}
	if (x == 1 && y == 1) {
		cout << "no path in Maze" << endl;
	}
	return 0;
};

为了将递归算法改为非递归算法,需要使用一个栈来存储在试探的过程中所走过的路径。一旦需要回退,可以从栈中取得刚才走过位置的座标和前进方向。栈中需要保存一系列三元组以记录这些信息。

struct items {
	int x, y, dir;
};

迷宫问题的非递归算法如下。

void path(int m, int p) {
	int i, j, d, g, h;
	mark[1][0] = 1;
	Stack<items> st(m * p);
	items tmp;
	st.Push(tmp);
	while (st.IsEmpty() == false) {
		st.Pop(tmp);
		i = tmp.x;
		j = tmp.y;
		d = tmp.dir;
		while (d < 8) {
			g = i + move[d].a;
			h = i + move[d].b;
			if (g == m && h == p) {
				cout << st;
				cout << m << " " << p << endl;
				return;
			}
			if (Maze[g][h] == 0 && mark[g][h] == 0) {
				mark[g][h] = 1;
				tmp.x = i;
				tmp.y = j;
				tmp.dir = d;
				st.Push(tmp);
				i = g;
				j = h;
				d = 0;
			} else {
				d ++;
			}
		}
	}
	cout << "no path" << endl;
};

ostream& operator << (ostream& os, items& item) {
	return os << item.x << "," << item.y << "," << item.dir;
};