2019.01.23 日间温暖,夜间生寒,萎靡的一天~
美食菜单如下
1,全排列介绍及实现(哈希映射应用)
2,基于全排列求N皇后全解
3,求解N皇后全解的优化及优化前后的时间对比
全排列介绍及实现
全排列大家应该都很熟悉,可如何高效编程实现或许有些陌生,以下将详细说明
设计思路
- 1,适用范围
输入: 1~n的自然数
输出:n个数的全排列所有情况 - 2,数据结构
数组P存储当前全排列
将需排列的数列作为数组下标(哈希映射),相应数组值表示状态(是否在全排列中)
const int maxn = 11;//假设最多只有11个数
//三个依次表示:n个数;P存储当前全排列;哈希表记录下标是否在全排列中
int n,P[maxn],hashTable[maxn] = {false};
- 3,递归设计
边界:index=n+1时表示一个全排列选取成功,输出后返回即可
递归式:当未到达边界时,扫描n个数是否已被选中,若已选中,扫描下一个;
若未选中,将x填入P中,同时将散列表赋真,继续填充下一个位置,结束时将相应散列表赋假
完整Code
#include<cstdio>
const int maxn = 11;
int n,P[maxn],hashTable[maxn] = {false};
//函数功能:给第index位填充数字
void generateP(int index)
{
//给第n+1填充,说明填充结束
if(index == n+1){
for(int i = 1; i <= n; i++){//打印输出
printf("%d",P[i]);
}
printf("\n");
return;
}
//选取一个未被选中的数填充第index位
for(int x = 1; x <= n; x++){
if(!hashTable[x]){//未被选中
P[index] = x;//填入
hashTable[x] = true;//哈希表状态改变
generateP(index+1);//填充下一个位置
hashTable[x] = false;//结束将位置让出来
}
}
}
int main()
{
n = 4;//改变此数字即可实现个数为1~11的全排列
generateP(1);
return 0;
}
测试
测试数据:n=3
测试结果
N皇后全解
该解法仅需稍微修改上述全排列算法即可,具体如下
求解N皇后最直接的思路是将N*N个点进行全排列,然后判断是否满足条件,但时间复杂度太高了。或者是利用栈来实现,类似深度搜索,可参考八皇后栈递归实现。
在这里假设当前摆放满足条件,将每一列的点对应的行数抽出必定为一个全排列(或者行列互换,由于对称性,不影响结果),所以,我们仅需求出行(N个点)的全排列,然后判断即可
这里由于是先求解出行的全排列,所以行列必不相同,仅需判断两点是否位于同一对角线(正/斜对角线)即可
判断函数
- 功能:判断两点是否在同一主/斜对角线上
- 返回:是–>true;否–>false
bool isOk(int ix,int iy,int jx,int jy)
{
//主、斜对角线
if(ix-jx == iy-jy || ix-jx == -iy+jy)return false;
else return true;
}
核心主体
在全排列基础上增加判断
任意一种摆放成立的情况下,其每一行对应的列数必定是一种全排列。所以我们只要将全排列求出来,再对其进行判断即可,由于筛选出来的行列必不相同,所以仅需判断是否位于同一对角线上即可
void generateP(int index)
{
if(index == n+1){
bool bflag = true;//标记是否满足条件
//对任意两个点判断
for(int i = 1; i <= n; i++){
for(int j = i+1; j <= n; j++){
if(!isOk(i,P[i],j,P[j])){//只要有一个不满足,立即跳出
bflag = false;
break;
}
}
}
//满足条件输出并统计总个数
if(bflag){
sum++;//统计解个数
for(int i = 1; i <= n; i++){
printf("%d",P[i]);
}
printf("\n");
}
return;
}
//全排列核心
for(int x = 1; x <= n; x++){
if(!hashTable[x]){
P[index] = x;
hashTable[x] = true;
generateP(index+1);
hashTable[x] = false;
}
}
}
算法优化
优化则是改变判断位置,每一次填充位置时对已有序列(1~index-1)进行判断即可,若不符合,立即跳转下一个数;若符合,继续判断
//优化代码
void generateP_Refined(int index)
{
if(index == n+1){
sum++;
for(int i = 1; i <= n; i++){
printf("%d",P[i]);
}
printf("\n");
return;
}
//全排列核心
for(int x = 1; x <= n; x++){
if(!hashTable[x]){
//优化判断,将x与1~index-1个数判断是否满足条件
bool bflag = true;//标记是否满足条件
for(int i = 1; i < index; i++){
if(!isOk(i,P[i],index,x)){//只要有一个不满足,立即跳出
bflag = false;
break;//若有一个不满足,立刻退出
}
}
if(!bflag)continue;//尝试下一个数
P[index] = x;
hashTable[x] = true;
generateP_Refined(index+1);
hashTable[x] = false;
}
}
}
时间对比
注释掉输出语句后的执行时间
完整Code
#include<cstdio>
#include<ctime>
const int maxn = 11;
int n,P[maxn],hashTable[maxn]={false};
int sum = 0;
//判断两点是否在同一主/斜对角线上
//是-->true;否-->false
bool isOk(int ix,int iy,int jx,int jy)
{
//主、斜对角线
if(ix-jx == iy-jy || ix-jx == -iy+jy)return false;
else return true;
}
//假设n皇后的一种摆放成立,其每一行对应的列数必定是一种全排列。所以我们只要将全排列求出来,再对其进行判断即可,
//由于筛选出来的行列必不相同,所以仅需判断是否位于同一对角线上即可
void generateP(int index)
{
if(index == n+1){
bool bflag = true;//标记是否满足条件
//对任意两个点判断
for(int i = 1; i <= n; i++){
for(int j = i+1; j <= n; j++){
if(!isOk(i,P[i],j,P[j])){//只要有一个不满足,立即跳出
bflag = false;
break;
}
}
}
//满足条件输出并统计总个数
if(bflag){
sum++;
for(int i = 1; i <= n; i++){
printf("%d",P[i]);
}
printf("\n");
}
return;
}
//全排列核心
for(int x = 1; x <= n; x++){
if(!hashTable[x]){
P[index] = x;
hashTable[x] = true;
generateP(index+1);
hashTable[x] = false;
}
}
}
//优化代码
void generateP_Refined(int index)
{
if(index == n+1){
sum++;
for(int i = 1; i <= n; i++){
printf("%d",P[i]);
}
printf("\n");
return;
}
//全排列核心
for(int x = 1; x <= n; x++){
if(!hashTable[x]){
//优化判断,将x与1~index-1个数判断是否满足条件
bool bflag = true;//标记是否满足条件
for(int i = 1; i < index; i++){
if(!isOk(i,P[i],index,x)){//只要有一个不满足,立即跳出
bflag = false;
break;//若有一个不满足,立刻退出
}
}
if(!bflag)continue;//尝试下一个数
P[index] = x;
hashTable[x] = true;
generateP_Refined(index+1);
hashTable[x] = false;
}
}
}
int main()
{
int startT,endT;
n = 8;
startT = clock();
generateP(1);
endT = clock();
printf("\n%d",sum);
printf("\n优化前时间:%d\n",endT-startT);
sum = 0;
startT = clock();
generateP_Refined(1);
endT = clock();
printf("\n%d",sum);
printf("\n优化后时间:%d",endT-startT);
return 0;
}