您的位置:首页 > 产品设计 > UI/UE

NQueens 与 递归,回溯

2015-11-09 18:45 337 查看

NQueens 问题描述:



规则:两个皇后 在同一行,同一列,同一斜线上都会打架 (即皇后不能放在同一行,同一列,同一斜线上)
上图是 8*8 棋盘放置8个皇后的一种方法示意图。
现有 n*n 棋盘,放 n 个皇后,提供算法,求一共有多少种不同的放置皇后的方法。


思路:(回阙法)

1)第一步有 n*n 个位置可以下,后面每一步,需要遍历棋盘,根据规则检验,确定位置。因此需要遍历棋盘。

2)每下一步前,都需要根据规则检验。这里可以用集合记录已放置皇后的位置(col,row),所在斜线(row+col, row-col)信息。

3)如果遍历完一次不满足条件,则需要将当前皇后信息都移出集合,进行下一次遍历试探。即回溯思想。

4)如果能顺利遍历完最后一行,最后一列,则解法count++;

下面是C#代码示例:

public class Solution {
ICollection<int> colSet = new HashSet<int>();  //column
ICollection<int> diaSet1 = new HashSet<int>(); //diagonal
ICollection<int> diaSet2 = new HashSet<int>();

public int TotalNQueens(int n) {
int count = TotalQueensHelper(0,0,n);
return count;
}

private int TotalQueensHelper(int row, int count, int n){
for(int col =0; col<n; col++){
if(colSet.Contains(col))
continue;
int dia1 = row + col;
if(diaSet1.Contains(dia1))
continue;
int dia2 = row - col;
if(diaSet2.Contains(dia2))
continue;

if(row == n-1){  //顺利遍历完
count ++;
}else{
colSet.Add(col);
diaSet1.Add(dia1);
diaSet2.Add(dia2);
count = TotalQueensHelper(row+1,count,n);
//
colSet.Remove(col);
diaSet1.Remove(dia1);
diaSet2.Remove(dia2);
}
}
return count;
}
}


一些关键点:

1) 二维数组中,同一斜线的数特性:row+col('/'该趋势斜线),row-col('\'该趋势斜线) 为同一个数
2) 函数调用,系统的处理工作:
当一个函数的运行期间调用另一个函数的时候,在运行被调用函数之前,系统都会先完成三件事:
(1)将所有的实在参数、返回地址等信息传递给被调用的函数保存;
(2)为被调用的局部变量分配存储区;
(3)将控制转移到被调用的函数的入口。
而从被调用函数返回调用函数之前,系统也应完成三件事:
(1)保存被调用函数的计算结果;
(2)释放被调用函数的数据区;
(3)依照被调用函数保存的返回地址将控制转移到调用函数上。
当有多个函数构成嵌套语句时,按照,“后调用先返回”的原则,上述的函数之间的信息传递和控制的转移必须通过“栈”来实现,即系统将整个程序运行时间所需的数据安排在一个栈中,每当调用一个函数时,就为它在栈顶分配一个存储区,每当一个函数退出时,就释放它的存储空间,则当前正在运行的函数的数据区必在栈顶。
3)  递归是函数调用的一种特殊调用。


private int TotalQueensHelper(int row, int count, int n){
for(int col =0; col<n; col++){
if(colSet.Contains(col))
continue;
int dia1 = row + col;
if(diaSet1.Contains(dia1))
continue;
int dia2 = row - col;
if(diaSet2.Contains(dia2))
continue;

if(row == n-1){  //顺利遍历完
count ++;
}else{
colSet.Add(col);
diaSet1.Add(dia1);
diaSet2.Add(dia2);
count = TotalQueensHelper(row+1,count,n);
//
colSet.Remove(col);
diaSet1.Remove(dia1);
diaSet2.Remove(dia2);
}
}
return count;
}




这里需要注意的是:

被调用函数,运行return后,是返回到调用函数继续执行。这里跟循环里的 return 退出循环不一样。

递归里的 return 是返回到上一层调用。

这里的 count = TotalQueensHelper(row+1,count,n); 是局部变量采用连续传递计数的,还可以采用全局变量。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 递归