回溯算法

搜索算法介绍

(1)穷举搜索(Exhaustive Search)

(2)盲目搜索(Blind or Brute-Force Search)

  • 深度优先(DFS)或回溯搜索(Backtracking);
  • 广度优先搜索(BFS);
  • 迭代加深搜索(Iterative Deepening);
  • 分枝限界法(Branch & Bound);
  • 博弈树搜索(α-β Search)

(3)启发式搜索(Heuristic Search)

  • A*算法和最佳优先(Best-First Search)
  • 迭代加深的A*算法
  • B*,AO*,SSS*等算法
  • Local Search, GA等算法

搜索空间的三种表示

  • 表序表示: 搜索对象用线性表数据结构表示;
  • 显式图表示: 搜索对象在搜索前就用图(树)的数据结构表示;
  • 隐式图表示: 除了初始结点,其他结点在搜索过程中动态生成.缘于搜索空间大,难以全部存储.

 

提高搜索效率的思考:随机搜索

  • 上世纪70年代中期开始, 国外一些学者致力于研究随机搜索求解困难的组合问题, 将随机过程引入搜索;
  • 选择规则是随机地从可选结点中取一个,从而可以从统计角度分析搜索的平均性能;
  • 随机搜索的一个成功例子:判定一个很大的数是不是素数, 获得了第一个多项式时间的算法;

 

回溯法:一个既带有系统性又带有跳跃性的搜索算法。

  • 系统性:它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。
  • 跳跃性:算法搜索至解空间树的任一结点时,判断该结点为根的子树是否包含问题的解,如果肯定不包含,则跳过以该结点为根的子树的搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。

这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题。

 

  • 搜索从开始结点(根结点)出发,以DFS搜索整个解空间。
  • 开始结点就成为一个活结点,同时也成为当前的扩展结点。在当前的扩展结点处向纵深方向移至一个新结点,并成为一个新的活结点,也成为当前扩展结点。
  • 如果在当前的扩展结点处不能再向纵深方向扩展,则当前扩展结点就成为死结点
  • 此时,应往回移动(回溯)至最近的一个活结点处,并使这个活结点成为当前的扩展结点;直至找到一个解或全部解。

 

回溯算法的基本步骤

  • (1)针对问题,定义问题的解空间(对解进行编码);
  • (2)确定易于搜索的解空间组织结构(按树或图组织解);
  • (3)以深度优先方式搜索解空间,搜索过程中裁减掉死结点的子树提高搜索效率。

注解:

(1)提高回溯法效率的两种方法

  • ①用约束函数剪去不满足约束的子树;
  • ②用限界函数剪去不能得到最优解的子树。

(2)两类常见的解空间树

  • 子集树:如0-1背包,叶结点数2n,总结点数2n+1,遍历时间为Ω(2n);
  • 排列树:如TSP问题,叶结点数n!,遍历时间为Ω(n!)。

回溯算法的一般框架:

1. 子集树回溯算法:

Backtrack(int t) {//搜索到树的第t层
//由第t层向第t+1层扩展,确定x[t]的值
if t>n thenoutput(x); //叶结点是可行解,输出解
else
while( all Xt)do { //Xt为所有x[t]的合法取值集
x[t]= Xt中第i个值;
if(Constraint(t) and Bound(t))
Backtrack(t+1);
}
}


执行时:Backtrack(1) //从1扩展并回溯

2. 排列树回溯算法:

Backtrack(int t) {//搜索到树的第t层
//由第t层向第t+1层扩展,确定x[t]的值
if t>n thenoutput(x); //叶结点是可行解,输出解
else
for i=t to n do {
swap(x[t], x[i]);
if(Constraint(t) andBound(t))
Backtrack(t+1);
swap(x[t], x[i]);
}
}


 

示例一:树和图的遍历

 

二叉树的遍历:

  • 先序遍历
  • 中序遍历
  • 后序遍历
  • 按层次遍历

图的遍历

  • 深度优先遍历
  • 广度优先遍历
  • 双向广度优先遍历

二叉树的遍历

二叉链表的存储结构

typedefstruct BiTnode {
ElemType data;
struct BiTnode *lchild, *rchild;
}*BiTree;


 

Preorder(BiTreeT) {
//递归程序
if T!=nil then {
visit(T);
Preorder(T->lchild);
Preorder(T->rchild);
}
}
 
 
 
Preorder(BiTreeT){
//非递归程序
if T=nil then return;
   inistack(S);push(S, T);
   while(!empty(S))do{
BiTree p=pop(S);
while(p!=nil) do{
visit(p);
push(S, p->rchild);
p=p->lchild;
}
}
}
 
BFSorder(BiTreeT){
//按层次遍历
if T=nil then return;
iniqueue(Q);
enqueeu(Q,T);
while(!empty(Q))do{
BiTree p=Dequeue(Q);
visit(p);
if p->lchild!=nil  then
 enqueue(Q,p->lchild);
if(p->rchild!=NIL) then
enqueue(Q,p->rchild);
}
}


 

图的遍历:

深度优先搜索(DFS)

DFS(v0){
v0.visited=True;
while(所有与v0邻接的顶点v and !v.visited ) do
DFS(v);
return;
}


 

广度优先搜索(BFS)

BFS(v0){
iniqueue(Q);
enqueue(Q,v0);
while(!empty(Q)) do {
p=dequeue(Q);
p.visited=True;
while(所有与p邻接的顶点v && !v.visited ) do
enqueue(Q, v);
}
}
 

双向广度优先搜索

对于迷宫等问题(S为入口,T为出口),可以采用双向广度优先搜索。S正向搜索,T反向搜索,当两个方向搜索在某层上生成同一结点时,即找到一条路径。算法比单向搜索的结点数少得多。

 

实例二:N后问题

在4×4棋盘上放上4个皇后,使皇后彼此不受攻击。不受攻击的条件是彼此不在同行(列)、斜线上。求出全部的放法。

 

解编码:(x1,x2,x3,x4)4元组,xi表示皇后i放在i行上的列号,如(3,1,2,4)

解空间:{(x1,x2,x3,x4)| xi∈S, i=1~4} S={1,2,3,4}

解空间树(满四叉树):


bool placetest(int k){
	int i = 1;
	for (; i<k; i++)
	if (X[i] == X[k] || abs(X[i] - X[k]) == abs(i - k))
		return false;
	return true;
}
void NQueen(int k){
	int i = 1;
	if (k>SIZE)
		print(X);
	else
	for (; i <= SIZE; i++){
		X[k] = i;
		if (placetest(k))
			NQueen(k + 1);
	}
}

求解时执行NQueen(1);

示例三:排列生成问题

给定正整数n,生成1,2,…,n所有排列

解空间树(排列树)

当n=3时


回溯算法
Backtrack(int t){
if t>n then output(x);
else
for i=t to n do{
swap(x[t], x[i]);
Backtrack(t+1);
swap(x[t], x[i]);
}
}

实例四:TSP问题

有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值

 

基本思想:

利用排列生成问题的回溯算法Backtrack(2),对x[]={1,2,…,n}的x[2..n]进行全排列,则(x[1],x[2]),(x[2],x[3]),…, (x[n],x[1])构成一个回路。在全排列算法的基础上,进行路径计算保存以及进行限界剪枝

 

TSPBacktrack(int i){
//cc记录(x[1],x[2]), …,(x[i-1],x[i])的距离和
if i>n then //搜索到叶结点,输出可行解与当前最优解比较{ 
if ( cc+a[x[n]][1]<bestv or bestv=∞) then{ 
bestv=cc+a[x[n]][1];
for j=1 to n do 
bestx[j]=x[j];
}
}else {
for j=i to n do 
if ( cc+a[x[i-1]][x[j]]<bestv or bestv=∞) then{ 
//限界裁剪子树
swap(x[i], x[j]);
cc+=a[x[i-1]][x[i]];
TSPBacktrack(i+1);
cc-=a[x[i-1]][x[i]];
swap(x[i], x[j]);
}
}
}

示例五:0-1背包问题

解空间树:


无限界函数的算法


KnapBacktrack(int i){
//cw当前背包重量,cv当前背包价值,bestv当前最优价值
if i>n then { 
//搜索到可行解
bestv=(bestv<cv)?cv:bestv;
output(x); 
}else {
if cw+w[i]<=c then{ 
//走左子树
x[i]=1; 
cw+=w[i]; 
cv+=v[i];
KnapBacktrack(i+1);
cw-=w[i]; 
cv-=v[i]; 
} 
//以下走右子树
{ x[i]=0; 
KnapBacktrack(i+1);
}
}//else
}//end



 



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值