递归一之:替代多重循环

递归(英语:Recursion),又译为递回,在数学与计算机科学中,是指在函数的定义中使用函数自身的方法。递归一词还较常用于描述以自相似方法
重复事物的过程。例如,当两面镜子相互之间近似平行时,镜中嵌套的图像是以无限递归的形式出现的。也可以理解为自我复制的过程。(以上定义来自维基百科[递归])

我们写程序用递归时常用于解决一下几种问题:

1,替代多重循环,特别是对于不同的问题规模有不同的循环次数。
2,解决本来就是递归定义的问题,相关的数学表达式递归定义的。比如阶乘函数,Fibonacci(斐波那契数列)数列,Ackerman(阿克曼)函数等。
3,可以将问题规模分解为更小规模进行求解的,或者说该问题的解是由更小规模的解构成的。

1,替代多重循环:

N皇后问题:

输入整数n,要求n个国际象棋的皇后,摆在n*n的棋盘上,互相不能攻击,输出全部可行的摆放方案。
对于摆放的皇后的位置其要求是:任两个皇后都不能处于同一条横行、纵行或斜线上。8皇后问题-维基百科

对于这道题,如果是给定的数比较小,我们自然而然的可以想到可能可以使用多重循环的方法,每一层循环判断一个位置符不符合。但是由于n的个数不确定,并且n可能比较大(比如n等于4,对于;使用多重循环都是不可取的了),这样我们就不好判断究竟要使用多少重循环了。

此时使用递归来替代多重循环就是一个解决这个问题的方案。具体做法可如下:

1,先摆放第一行的那个皇后,对应n个。此时有n中可能的结果。此时第一行的这个皇后相当于是确定下来的了。
2,接着摆放第2行的那个皇后,注意此时第二行的这个皇后需要和第一行的皇后满足题目的要求。由于第一行已经确定,那么第二行也会被确定下来。
3,依次类推,当第k行和第k-1行没有冲突时,则继续往下递归,否则的话应该从头开始进行递归遍历。

下面是C++的代码实现:

#include <iostream>
#include <cmath>
using namespace std;

int N;		
//用来存放第i行的皇后放在那一列,里面的值表示哪一列,从0开始 
int queenPos[100]; 

//表示第k行之前的皇后已经摆放完成了 
void NQueen(int k);

int main() {
    
    
	cin >> N; 
	NQueen(0);
	return 0;
}

void NQueen(int k) {
    
    	//在k-1行摆放好的情况下摆放第k行 
	if (k == N) {
    
    		//k=N则证明找到一个可能的情况,输出
		for (int i = 0; i < N; i++) {
    
    
			cout << queenPos[i] + 1 << " ";
		}
		cout << endl;
	}
	for (int i = 0; i < N; i++) {
    
    //逐个尝试第K个皇后的位置,这里的i表示的列 
		int j; 
		for (j = 0; j < k; j++) {
    
    //和已经摆好的k个皇后的位置进行比较,看是否冲突,这里只遍历到k -1
		 	if (queenPos[j] == i || 
			 	abs(queenPos[j] - i) == abs(k - j)) {
    
    //行之差和列之差相等为对角线,queenPos[j]-i表示列之差,k-j表示行之差。 
			 		break;
			 } 	
		}
		if (j == k) {
    
    	//遍历过程中没有中途退出时则表明第k行中第i列可行
			queenPos[k] = i;
			NQueen(k + 1);	//可行则递归k+1
		} 
	} 
}

运行结果:8皇后问题有92总可能的结果,4皇后有2总可能的结果。

关于递归的其他内容可以查看:
递归二之:解决递归定义的问题
递归三之:将问题分解为更小规模

猜你喜欢

转载自blog.csdn.net/weixin_44184990/article/details/105107049
今日推荐