▲图论中,树的遍历方式有广度优先遍历和深度优先遍历两种方式
广度优先遍历(BFS):从图的一个未遍历的结点出发 先遍历这个结的相邻结点 再依次遍历每个相邻结点的相邻结点(即一层一层遍历)
如:
深度优先遍历(DFS):从图中一个未访问的顶点 V 开始 沿着一条路一直走到底 然后从这条路尽头的节点回退到上一个结点 再从另一条路开始走到底 不断重复此过程 直到所有的点都遍历完成
如:
从根节点1开始,与其相邻的结点有2、3、4.先遍历结点2,再向下遍历结点5和9((1)过程).当前结点9没有子节点.此时就从结点9回退到上一个结点5,判断结点5是否还有除9以外的结点,没有继续回退到2,2也没有除5以外的结点,回退到 1,1有除2以外的结点3,所以从结点3开始进行深度优先遍历(开始(2)过程).以此类推,直至遍历结束.
通常使用非递归法进行DFS:借助栈结构,根结点进栈,其子结点的右结点进栈,再其根结点的左结点进栈.
如:对下面二叉树深度优先遍历
根结点A入栈,A出栈,C、B入栈,B出栈,E、D入栈、D出栈,E出栈,C出栈,G、F入栈,F出栈,G出栈。(A B D E C F G)
序号 | 栈 | 结果 |
---|---|---|
1 | A | |
2 | A | |
3 | C B | A |
4 | A B | |
5 | C E D | A B |
6 | C | A B D E |
7 | A B D E C | |
8 | G F | A B D E C |
9 | A B D E C F G |
▲状态空间树:描述解空间的树形结构
树中每个结点称为一个问题状态
从根到树中某个状态的路径代表一个作为候选解的元组称为该状态的解状态
从根到某个解状态的路径作为可行解的元组称为该解状态为答案状态
▲剪枝函数的深度优先生成的状态空间树中结点的求解方法称为回溯法。通俗地说,回溯法就是在遍历过程中,"感觉到"原先选择不优或达不到目标时"回头"至上一步重新选择
广度优先生成的结点 并使用剪枝函数的方法称为分支限界法
★n皇后问题:n×n棋盘上放置n个皇后,使得其中任意两个都不在同行同列同一斜线
分析:由于每个皇后不应在同一行,为不失一般性,假定第i个皇后放在第i行(0≤i<n),设queen[i]为第i行皇后所处列数.易知隐式约束条件为∀i,j∈[0,n),i≠j(不同行),queen[i]≠queen[j](不同列),|i-j|≠|queen[i]-queen[j]|(不同斜线)
#include <iostream>
#include <cmath>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
const int maxn=16;//上限(可自行更改)
int queen[maxn],sum=0;
//定义全局变量sum表可行解的个数 queen[i]表可行列数
void display(int* queen,int n){
for(int i=0;i<n;i++){
cout<<"("<<i<<" "<<queen[i]<<")";
}
cout<<endl;
sum++;
}
bool check(int cur){
for(int i=0;i<cur;i++){//从第0行开始向下 故约束条件中可不需要不同行的约束条件
if(queen[i]==queen[cur]||abs(i-cur)==abs(queen[i]-queen[cur]))//约束条件:不同列不同斜线
return false;
}
return true;
}
void dfs(int cur,int n){//cur为当前遍历层数(行数)
if(n<=0||n>maxn){//不合法或越界情况
return;
}
if(cur==n){//遍历至最后层 直接输出
display(queen,n);
}
else{
for(int i=0;i<n;i++){
queen[cur]=i;
if(check(cur))//若满足约束条件 继续下一层遍历
dfs(cur+1,n);
}
}
}
int main(int argc, char** argv) {
int n;
cin>>n;
dfs(0,n);
cout<<"The total of "<<n<<" Queen's solutions:"<<sum;
return 0;
}
当n=8时结果为:
补:《算法设计与分析》陈慧南版 n皇后问题代码:
#include <iostream>
#include <cmath>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
#define N 4 //4皇后问题代码(可更改)
int x[N];
bool Place(int k,int i,int* x){//判定两个皇后是否在同一列或在同一斜线上
for(int j=0;j<k;j++){
if((x[j]==i)||(abs(x[j]-i)==abs(j-k)))//约束条件:不同列不同斜线
return false;
}
return true;
}
void NQueens(int k,int* x){//递归函数求解n皇后问题
for(int i=0;i<N;i++){//尝试所有可选列数
if(Place(k,i,x)){//满足约束条件
x[k]=i;
if(k==N-1){//末行成功 输出列
for(int j=0;j<N;j++)
cout<<x[j]<<" ";
cout<<endl;
}
else//未遍历至末行继续向下遍历
NQueens(k+1,x);
}
}
}
int main(int argc, char** argv) {
NQueens(0,x);
return 0;
}