搜档网
当前位置:搜档网 › 经典的深搜算法

经典的深搜算法

bensenoier的快乐天堂
vijos 1128选数!!!——经典的深搜算法,记住!!!!2009-06-29 14:54描述 Description

已知 n 个整数 x1,x2,…,xn,以及一个整数 k(k<n)。从 n 个整数中任选 k 个整数相加,可分别得到一系列的和。例如当 n=4,k=3,4 个整数分别为 3,7,12,19 时,可得全部的组合与它们的和为:
3+7+12=22 3+7+19=29 7+12+19=38 3+12+19=34。
现在,要求你计算出和为素数共有多少种。
例如上例,只有一种的和为素数:3+7+19=29)。

输入格式 Input Format
n , k (1<=n<=20,k<n)
x1,x2,…,xn (1<=xi<=5000000

输出格式 Output Format
一个整数(满足条件的种数)。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

program ll;
var
s,zong,m,p,k,n,i:longint;
a:array[0..100000] of longint;
function pan(q:longint):boolean;
var
j:integer;
begin
for j:=2 to trunc(sqrt(q)) do
if q mod j = 0 then exit(false); /////////判断素数!!!!
exit(true);
end;
procedure run(x:longint); ////////////////////这里的x表示总的量,x最大是n,含义是从n个数中一一寻
begin ///////////////////找,if 当前找到的第x个数大于n,则是边界,跳出!!!!
if x>n then exit
else
begin
s:=s+a[x];
inc(m);
if m<k then run(x+1)
else
if pan(s) then inc(zong); /////////////这里是深搜的经典算法,因为从一到n选数,

s:=s-a[x]; //////////当前状态只有两种选择,一是选这个数,二是不选!!
dec(m);
run(x+1);
end;
end;
begin
readln(n,k);
for i:=1 to n do
read(a[i]);
zong:=0;m:=0;s:=0;
run(1);
writeln(zong);
end.

这种深搜做法很棒,就是从第一个到最后一个,如果用循环也可以!!!!!!



搜索方法小结 (算法原码吧)
2008-05-21 19:38文章由 算法源码吧(https://www.sodocs.net/doc/6b10520556.html,)收集

不管哪种搜索,都统一用这样的形式表示:搜索的对象是一个图,它面向一个问题,不一定有明确的存储形式,但它里面的一个结点都有可能是一个解(可行解),搜索的目的有两个方面,或者求可行解,或者从可行解集中求最优解,我们用两张表来进行搜索,一个叫OPEN表,表示那些已经展开但还没有访问的结点集,另一个叫CLOSE表,表示那些已经访问的结点集。

一、蛮力搜索(DFS,BFS)
DFS和BFS是最基本的搜索算法,用上面的形式表示它们是非常相似的。

BFS(Breadth-First-Search 宽度优先搜索)

首先将起始结点放入OP

EN表,CLOSE表置空,算法开始时:
1、如果OPEN表不为空,从表中开始取一个结点S,如果为空算法失败
2、S是目标解吗?是,找到一个解(继续寻找,或终止算法);不是到3
3、将S的所有后继结点展开,就是从S可以直接关联的结点(子结点),如果不在CLOSE表中,就将它们放入OPEN表末尾,并把S放入CLOSE表,重复算法到1

DFS(Depth-First-Search 深度优先搜索)
首先将起始结点放入OPEN表,CLOSE表置空,算法开始时:
1、如果OPEN表不为空,从表中开始取一个结点S,如果为空算法失败
2、S是目标解吗?是,找到一个解(继续寻找,或终止算法);不是到3
3、将S的所有后继结点展开,就是从S可以直接关联的结点(子结点),如果不在CLOSE表中,就将它们放入OPEN表开始,并把S放入CLOSE表,重复算法到1

没看出有什么不同?擦干净了眼镜再看一遍吧。

知道BFS和DFS有什么不同吗?B和D嘛。-_-!

仔细观察OPEN表中待访问的结点的组织形式,BFS是从表头取结点,从表尾添加结点,也就是说OPEN表是一个队列,没错,BFS首先就要让你想到‘队列’;而DFS,它是从OPEN表头取结点,也从表头添加结点,也就是说OPEN表是一个栈!

DFS用到了栈,所以有一个很好的实现方法,那就是递归,系统栈是计算机程序中极重要的部分之一,你不想递归?怕它用完了?放那儿也是浪费。而且用递归也有个好处就是,在系统栈中只需要存结点最大深度那么大的空间,也就是在展开一个结点的后续结点时可以不用一次全部展开,用一些环境变量记录当前的状态,在递归调用结束后继续展开。

利用系统栈实现的DFS
函数 dfs(结点 s)
{
s超过最大深度了吗?是:相应处理,返回;
s是目标结点吗?是:相应处理;否则:
{
s放入CLOSE表;
for(c=s.第一个子结点 ;c不为空 ;c=c.下一个子结点() )
if(c不在CLOSE表中)
dfs(c);递归
}
}

如果指定最大搜索深度为n,那系统栈最多使用n个单位,它相当于有状态指示的OPEN表,状态就是c,在栈里存了前面搜索时的中间变量c,在后面的递归结束后,c继续后移。在象棋等棋类程序中,就是用这样的DFS的基本模式搜索棋局局面树的,因为如果用OPEN表,有可能还没完成搜索OPEN表就暴满了,这是难于控制的情况。

我们说DFS和BFS都是蛮力搜索,因为它们在搜索到一个结点时,在展开它的后续结点时,是对它们没有任何‘认识’的,它认为它的孩子们都是一样的‘优秀’,但事实并非如此,后续结点是有好有坏的。好,就是说它离目标结点‘近’,如果优先处理它,就会更快的找到目标结点,从而整体上提高搜索性能。



二、启发式搜索
为了改善上面的算法,我们需要对展开后续结点时对子结点有所了解,这里需要一个估值函数,估值函数就是评价函数,它用来评价子结点的好坏,因为准确评价是不可能的,所以称为估值。打个比方,估值函数就像一台显微镜,一双‘慧眼’,它能分辨出看上去一样的孩子们的手,哪个很脏,有细菌,哪个没有,很干净,然后对那些干净的孩子进行奖励。(我不是老师,不会打比方,呵)对,这里似乎需要‘排序’,排序是要有代价的,而花时间做这样的工作会不会对整体搜索效率有所帮助呢,这完全取决于估值函数。

排序,怎么排?翻翻《数据结构》排序算法一大堆,用哪一个?你会很喜欢高级的排序算法,快排吧,qsort!不一定,要看要排多少结点,如果很少,简单排序法就很OK了。看具体情况了。

排序可能是对OPEN表整体进行排序,也可以是对后续展开的子结点排序,排序的目的就是要使程序有启发性,更快的搜出目标解。

如果估值函数只考虑结点的某种性能上的价值,而不考虑深度,比较有名的就是有序搜索(Ordered-Search),它着重看好能否找出解,而不看解离起始结点的距离(深度)。如果估值函数考虑了深度,或者是带权距离(从起始结点到目标结点的距离加权和),那就是A*,举个问题例子,八数码问题,如果不考虑深度,就是说不要求最少步数,移动一步就相当于向后多展开一层结点,深度多算一层,如果要求最少步数,那就需要用A*。简单的来说A*就是将估值函数分成两个部分,一个部分是路径价值,另一个部分是一般性启发价值,合在一起算估整个结点的价值,具体A*以后再谈。

从A*的角度看前面的搜索方法,如果路径价值为0就是有序搜索,如果路径价值就用所在结点到起始结点的距离(深度)表示,而启发值为0,那就是BFS或者DFS,它们两刚好是个反的,BFS是从OPEN表中选一个深度最小的进行展开,而DFS是从OPEN表中选一个深度最大的进行展开,它们俩还真是亲兄弟啊!当然只有BFS算是特殊的A*,所以BFS可以求要求路径最短的问题,只是没有任何启发性。

上一篇:哎,,,地震啊,地震
下一篇:贪心算法::启发式搜索


贪心算法::启发式搜索
2008-05-21 19:40文章由 算法源码吧(https://www.sodocs.net/doc/6b10520556.html,)收集

蛮干算法的成功完全是借助于计算机运算的快速,如果问题的解比较少的时候使用起来是比较容易的。但当问题的解比较多,则不宜使用,常用的做法是剪枝,剪枝是一种形象的描述,因为按深搜的算法,图可以描述为与之对应的树或森林,而剪枝的意思就是去掉某些子树,为什么

要去掉,这里要
用到一个剪枝判断,判断的方法是具体问题具体分析,但是有一点是要考虑到的,剪枝的高效性是建立在判断的额外开销上的,如果这里的开销大,则剪枝只会宣告失败。

而更好的做法是运用“贪心策略”。

【贪心算法】
贪心算法(也叫贪婪算法)不是某种特定的算法,而是一类抽象的算法,或者说只是一种思想,它的具体表现在,对解空间进行搜索时,不是机械地搜索,而是对局部进行择优选取,贪心算法的目的不是为了找到全部解,也当然找不出最优解,而只是找出一种可行解,这样就会得到惊人的高效性。因此,贪心算法也叫启发式搜索,这种启发就是所谓的“贪心策略”。

以马踏棋盘问题为例,问题描述:在国际象棋的棋盘上指定一个初始马位置,一匹马从这个位置出发,经过不重复的走动,直到踏遍整个棋盘,输出一种可行路径。

对8×8的棋盘来说,马踏棋盘的解是一个天文数字,相当之多,而采用蛮干算法,求一个解的时候会非常吃力,因此采用贪心算法。这里选取的贪心策略是,在某个马位置顶点的后继顶点(子结点)中,择优选取那些出口更小的顶点进行搜索,出口的意思就是这个点能跳到的可行位置的路径数,这样的贪心策略是容易被人接受的,一开始往出口少的点跳,则往后出口多的点就多,能跳通的可能性就大,而事实也证明了,如果采用这样的策略在求一个解时几乎不需要回溯,对于更大的棋盘也如此。

C++代码:在(VC6上调试通过)

#include "stdio.h"
class horse
{
public:
horse(int,int);
~horse();
void solve(int,int);
protected:
void dfs(int,int,int);
int **data;
int *head;
int width;
int height;
int size;
int count;
};
struct hnode
{
int x;
int y;
int weight;
};
horse::horse(int n,int m)
{
width=n;
height=m;
size=n*m;
head=new int[size];
data=new int*[m];
for(int i=0;i<m;++i)
{
data[i]=head+i*n;
for(int j=0;j<n;++j)
data[i][j]=0;
}
}
horse::~horse()
{
delete[] data;
delete[] head;
}
void horse::solve(int x,int y)
{
try
{
count=0;
dfs(x,y,1);
printf("无解!\n回溯%d次\n",count);
}
catch(int)
{
for(int i=0;i<height;++i)
{
printf("\n");
for(int j=0;j<width;++j)
printf(" %02d",data[i][j]);
}
printf("\n回溯%d次\n",count);
}
}
void horse::dfs(int x,int y,int c)
{
static int dx[8]={-1,-1,1,1,-2,-2,2,2};
static int dy[8]={-2,2,-2,2,-1,1,-1,1};
hnode hn[8];
data[y][x]=c;
if(c==size)throw(1);
for(int i=0;i<8;++i)
{
int tx,ty;
hn[i].x=tx=x+dx[i];
hn[i].y=ty=y+dy[i];
if(tx<0||tx>=width||ty<0||ty>=height||data[ty][tx]>0)
{
hn[i].weight=-1;continue;
}
hn[i].weight=0;
for

(int j=0;j<8;++j)
{
int mx,my;
mx=tx+dx[j];
my=ty+dy[j];
if(mx>=0&&mx<width&&my>=0&&my<height&&data[my][mx]==0)
hn[i].weight++;
}
if(hn[i].weight==0)

hn[i].weight=9;
}
for(i=0;i<7;++i)
for(int j=i+1;j<8;++j)
if(hn[i].weight>hn[j].weight)
{
hnode temp=hn[i];
hn[i]=hn[j];
hn[j]=temp;
}
for(i=0;i<8;++i)
if(hn[i].weight>0)
dfs(hn[i].x,hn[i].y,c+1);
data[y][x]=0;
++count;//回溯次数
}
void main()
{
horse a(8,9);//width=8 * height=9的盘棋
a.solve(0,0);//初始棋子位置
}

上一篇:搜索方法小结 (算法原码吧)
下一篇:穷举搜索::回溯与深搜 && 禁忌搜...


穷举搜索::回溯与深搜 && 禁忌搜索算法简介2008-05-21 19:40
文章由 算法源码吧(https://www.sodocs.net/doc/6b10520556.html,)收集

计算机常用算法大致有两大类,一类叫蛮干算法,一类叫贪心算法,前者常使用的手段就是搜索,对全部解空间进行地毯式搜索,直到找到指定解或最优解。
【建立解空间】
问题的解应该如何描述,如何建立?借助图论的思想,我们可以用图来描述,图的定义为G<V,E>,由顶点集和边集构成,顶点即实实在在的数据、对象,而边可以抽象为关系,即顶点间的关系,这种关系不一定非要在数据结构上表现出来,用数据结构的语言来描述,如果关系是一对一,则为线性表,如果关系是一对多,则为树,如果关系是多对多,则为图,如果完全没有关系,则为集合。但在数据结构中这种关系不一定非要在数据的存储性质上一开始就表现出来,譬如,你可以用一个数组表示一个线性表,也可以表示完全二叉树,同样也可以用邻接表表示一个图,对于关系的描述不是数据结构本身的描述,而是算法的描述,正如数据结构是离不开特定的算法一样,不可分开单独而谈。描述了解,那如何建立解空间,即如何对图进行搜索?
【深度优先搜索】
(Depth First Search)是用栈的机制对图的顶点进行深度优先的搜索。简易流程如下:
DFS(V0(起始顶点))
访问V0
for(v=V0的第一个子结点 到 最后一个子结点(边所对的顶点))
如果v未被访问,DFS(v);
搜索过程是先往结点深处搜索,一旦有子结点未访问就向下遍历。这样的方法类似回溯算法,先往下试探,如果不行出退回(回溯)。

【回溯】
回溯的经典例子是8皇后问题。
例:在国际象棋地图上(8×8)放上8个皇后,使任意两个皇后都不在同一行或同一列或同一斜行,找出所有解。
每一个皇后的位置可以认为是一个顶点,而皇后之间不在同一行或同一列或同一斜行的性质认为是顶点之间的关系,我们可以用回溯试探的方法考虑:先依次试探每一个皇

后的位置,如果有不满足条件的情况则退回,直到完成所有解的计数和输出。

用数组存储:int pos[8];
pos[0]表示第一个皇后的位置(0,1,...7)依次类推。
流程:
dfs(c)//c从0开始
for(v=0;v<8;++v)
如果pos[c]:=v满足条件,dfs(c+1);

退回之后pos[c]:=0;
这跟书上的回溯算法不太一样,因为是采用深搜的方法写的,其实思想是一致的,要仔细体会。

附N皇后C语言代码:
/*p118.4*/
/*N Queen Problem*/
#include<stdio.h>
#define MAX 100
int s[MAX];
int n;
int m1[MAX],m2[MAX],m3[MAX];
long count=0;
void output_solution()
{
int i,j;
printf("No.%d\n",++count);
for(i=0;i<n;++i)
{
for(j=0;j<s[i];++j)printf(" ");
printf("Q\n");
}
printf("---------------\n");
}
void dfs(int c)
{
int i;
if(c==n)
{
/*output_solution();*/
++count;
}
else
{
for(i=0;i<n;++i)
{
if(m1[i]==0&&m2[c+i]==0&&m3[n+i-c-1]==0)
{
s[c]=i;
m1[i]=1;
m2[c+i]=1;
m3[n+i-c-1]=1;
dfs(c+1);
m1[i]=0;
m2[c+i]=0;
m3[n+i-c-1]=0;
}
}
}
}
void main()
{
scanf("%d",&n);
dfs(0);
printf("total:%d\n",count);
}

文章由 算法源码吧(https://www.sodocs.net/doc/6b10520556.html,)收集
忌搜索(Tabu Search或Taboo Search,简称TS)的思想最早由Glover(1986)提出,它是对局部领域搜索的一种扩展,是一种全局逐步寻优算法,是对人类智力过程的一种模拟。TS算法通过引入一个灵活的存储结构和相应的禁忌准则来避免迂回搜索,并通过藐视准则来赦免一些被禁忌的优良状态,进而保证多样化的有效探索以最终实现全局优化。相对于模拟退火和遗传算法,TS是又一种搜索特点不同的 meta-heuristic算法。迄今为止,TS算法在组合优化、生产调度、机器学习、电路设计和神经网络等领域取得了很大的成功,近年来又在函数全局优化方面得到较多的研究,并大有发展的趋势。本章将主要介绍禁忌搜索的优化流程、原理、算法收敛理论与实现技术等内容。
1. 引言 局部领域搜索是基于贪婪思想持续地在当前解的领域中进行搜索,虽然算法通用易实现,且容易理解,但其搜索性能完全依赖于领域结构和初解,尤其窥陷入局部极小而无法保证全局优化性。针对局部领域搜索,为了实现全局优化,可尝试的途径有:以可控性概率接受劣解来逃逸局部极小,如模拟退火算法;扩大领域搜索结构,如TSP的2opt扩展到k-opt;多点并行搜索,如进化计算;变结构领域搜索( Mladenovic et al,1997);另外,就是采用TS的禁忌策略尽量避免迂回搜索,它是一种确定性的局部极小突跳策略。
禁忌搜索是人工智能的一种体现,是局部领域搜索的一种扩展。禁忌搜索最重要的思想是标记对应

已搜索的局部最优解的一些对象,并在进一步的迭代搜索中尽量避开这些对象(而不是绝对禁止循环),从而保证对不同的有效搜索途径的探索。禁忌搜索涉及到领域(neighborhood)、禁忌表(tabu list)、禁忌长度(tabu 1ength)、候选解(candidate)、藐视准则(candidate)等概念,我们首先用一个示例来理解禁忌搜索及其各重要概念,而后给
出算法的一般流程。
2.禁忌搜索示例 组合优化是TS算法应用最多的领域。置换问题,如TSP、调度问题等,是一大批组合优化问题的典型代表,在此用它来解释简单的禁忌搜索算法的思想和操作。对于 n元素的置换问题,其所有排列状态数为n!,当n较大时搜索空间的大小将是天文数字,而禁忌搜索则希望仅通过探索少数解来得到满意的优化解。
首先,我们对置换问题定义一种邻域搜索结构,如互换操作(SWAP),即随机交换两个点的位置,则每个状态的邻域解有Cn2=n(n一1)/2个。称从一个状态转移到其邻域中的另一个状态为一次移动(move),显然每次移动将导致适配值(反比于目标函数值)的变化。其次,我们采用一个存储结构来区分移动的属性,即是否为禁忌“对象”在以下示例中:考虑7元素的置换问题,并用每一状态的相应21个邻域解中最优的5次移动(对应最佳的5个适配值)作为候选解;为一定程度上防止迂回搜索,每个被采纳的移动在禁忌表中将滞留3步(即禁忌长度),即将移动在以下连续3步搜索中将被视为禁忌对象;需要指出的是,由于当前的禁忌对象对应状态的适配值可能很好,因此在算法中设置判断,若禁忌对象对应的适配值优于“ best so far”状态,则无视其禁忌属性而仍采纳其为当前选择,也就是通常所说的藐视准则(或称特赦准则)。
可见,简单的禁忌搜索是在领域搜索的基础上,通过设置禁忌表来禁忌一些已经历的操作,并利用藐视准则来奖励一些优良状态,其中领域结构、候选解、禁忌长度、禁忌对象、藐视准则、终止准则等是影响禁忌搜索算法性能的关键。需要指出的是:
(1)首先,由于TS是局部领域搜索的一种扩充,因此领域结构的设计很关键,它决定了当前解的领域解的产生形式和数目,以及各个解之间的关系。
(2)其次,出于改善算法的优化时间性能的考虑,若领域结构决定了大量的领域解(尤其对大规模问题,如TSP的SWAP操作将产生Cn2个领域解),则可以仅尝试部分互换的结果,而候选解也仅取其中的少量最佳状态。
(3)禁忌长度是一个很重要的关键参数,它决定禁忌对象的任期,其大小直接进而影响整个算法的搜索

进程和行为。同时,以上示例中,禁忌表中禁忌对象的替换是采用FIFO方式(不考虑藐视准则的作用),当然也可以采用其他方式,甚至是动态自适应的方式。
(4)藐视准则的设置是算法避免遗失优良状态,激励对优良状态的局部搜索,进而实现全局优化的关键步骤。
(5)对于非禁忌候选状态,算法无视它与当前状态的适配值的优劣关系,仅考虑它们中间的最佳状态为下一步
决策,如此可实现对局部极小的突跳(是一种确定性策略)。
(6)为了使算法具有优良的优化性能或时间性能,必须设置一个合理的终止准则来结束整个搜索过程。
此外,在许多场合禁忌对象的被禁次数(frequency)也被用于指导搜索,以取得更大的搜索空间。禁忌次数越高,通常可认为出现循环搜索的概率越大。
3.禁忌搜索算法流程 通过上述示例的介绍,基本上了解了禁忌搜索的机制和步骤。简单TS算法的基本思想是:给定一个当前解(初始解)和一种邻域,然后在当前解的邻域中确定若干候选解;若最佳候选解对应的目标值优于“best so far”状态,则忽视其禁忌特性,用其替代当前解和“best so far”状态,并将相应的对象加入禁忌表,同时修改禁忌表中各对象的任期;若不存在上述候选解,则选择在候选解中选择非禁忌的最佳状态为新的当前解,而无视它与当前解的优劣,同时将相应的对象加入禁忌表,并修改禁忌表中各对象的任期;如此重复上述迭代搜索过程,直至满足停止准则。
条理化些,则简单禁忌搜索的算法步骤可描述如下:
(1)给定算法参数,随机产生初始解x,置禁忌表为空。
(2)判断算法终止条件是否满足?若是,则结束算法并输出优化结果;否则,继续以下步骤。
(3)利用当前解工的邻域函数产生其所有(或若干)邻域解,并从中确定若干候选解。
(4)对候选解判断藐视准则是否满足?若成立,则用满足藐视准则的最佳状态y替代x成为新的当前解,即x=y,并用与y对应的禁忌对象替换最早进入禁忌表的禁忌对象,同时用y替换“best so far”状态,然后转步骤6;否则,继续以下步骤。
(5)判断候选解对应的各对象的禁忌属性,选择候选解集中非禁忌对象对应的最佳状态为新的当前解,同时用与之对应的禁忌对象替换最早进入禁忌表的禁忌对象元素。
(6)转步骤(2)。
同时,上述算法可用如下流程框图更直观地描述,如图4.1.1。

我们可以明显地看到,邻域函数、禁忌对象、禁忌表和藐视准则,构成了禁忌搜索算法的关键。其中,邻域函数沿用局部邻域搜索的思想,用于实现邻域搜索

;禁忌表和禁忌对象的设置,体现了算法避免迂回搜索的特点;藐视准则,则是对优良状态的奖励,它是对禁忌策略的一种放松。需要指出的是,上述算法仅是一种简单的禁忌搜索框架,对各关键环节复杂和多样化的设计则可构造出各种禁忌搜索算法。同时,算法流程中的禁忌对象,可以是搜索状态,也可以是特定搜索操作,甚至是搜索目标值等。
同时,与传统的优化算法相比,TS算法的主要特点是:
(1)在搜索
过程中可以接受劣解,因此具有较强的“爬山”能力;
(2)新解不是在当前解的邻域中随机产生,而或是优于“best so far”的解,或是非禁忌的最佳解,因此选取优良解的概率远远大于其他解。由于TS算法具有灵活的记忆功能和藐视准则,并且在搜索过程中可以接受劣解,所以具有较强的“爬山”能力,搜索时能够跳出局部最优解,转向解空间的其他区域,从而增强获得更好的全局最优解的概率,所以TS算法是一种局部搜索能力很强的全局迭代寻优算法。

上一篇:贪心算法::启发式搜索
下一篇:用邻接矩阵表示的图的广度优先搜...
相关文章:? 使用禁忌搜索法求解约瑟夫环改进...
? 智能算法——模拟退火,遗传算法,...
? 智能算法:模拟退火/遗传算法/禁...
? 基于改进的遗传禁忌搜索算法求解...
? 禁忌搜索算法
? 禁忌搜索算法简介
? 智能算法学习笔记-模拟退火,遗传...


用邻接矩阵表示的图的广度优先搜索算法 深度优先搜索算法
2008-05-21 19:45文章由 算法源码吧(https://www.sodocs.net/doc/6b10520556.html,)收集
/* 用邻接矩阵表示的图的广度优先搜索算法*/

#include<stdio.h>
#include<stdlib.h>

#define MAXVEX 6
#define MAX 0

typedef char VexType;
typedef float AdjType;

typedef struct {
int n; /* 图的顶点个数 */
/*VexType vexs[MAXVEX]; 顶点信息 */
AdjType arcs[MAXVEX][MAXVEX]; /* 边信息 */
} GraphMatrix;

#define MAXNUM 8/* 队列中最大元素个数 */
typedef int DataType;
struct SeqQueue { /* 顺序队列类型定义 */
int f, r;
DataType q[MAXNUM];
};

typedef struct SeqQueue *PSeqQueue; /* 顺序队列类型的指针类型 */

PSeqQueue createEmptyQueue_seq( void ) {
PSeqQueue paqu;
paqu = (PSeqQueue)malloc(sizeof(struct SeqQueue));
if (paqu == NULL)
printf("Out of space!! \n");
else
paqu->f = paqu->r = 0;

return paqu;
}

int isEmptyQueue_seq( PSeqQueue paqu ) {
return paqu->f == paqu->r;
}

/* 在队列中插入一元素x */
void enQueue_seq( PSeqQueue paqu, DataType x ) {
if( (paqu->r + 1) % MAXNUM == paqu->f )
printf( "Full queue.\n" );
else {
paqu->q[paqu->r] = x;
paqu->r = (paqu->r + 1) % MAXNUM;
}
}

/* 删

除队列头部元素 */
void deQueue_seq( PSeqQueue paqu ) {
if( paqu->f == paqu->r )
printf( "Empty Queue.\n" );
else
paqu->f = (paqu->f + 1) % MAXNUM;
}

/* 对非空队列,求队列头部元素 */
DataType frontQueue_seq( PSeqQueue paqu ) {
return (paqu->q[paqu->f]);
}

#define NON -1

int firstVertex(GraphMatrix* pgraph) {
if (pgraph->n == 0)
return NON;
else return 0;

}

int nextVertex(GraphMatrix* pgraph,int n) {
if (n == pgraph->n-1)
return NON;
else return n + 1;
}

int firstAdjacent(GraphMatrix* pgraph, int i) {
int k;
for (k = 0; k < pgraph->n; k++)
if(pgraph->arcs[i][k] != 0) return k;
return NON;
}


int nextA
djacent(GraphMatrix* pgraph, int i, int j) {
int k;
for (k = j+1; k < pgraph->n; k++)
if (pgraph->arcs[i][k] != 0) return k;
return NON;
}

typedef int Vertex;
#define TRUE 1
#define FALSE 0

int visited[MAXVEX];

void bfs ( GraphMatrix* g , Vertex v );
void bft ( GraphMatrix* g ) {
Vertex v ;
for ( v =firstVertex ( g ) ; v != NON ; v = nextVertex ( g , v ) )
if ( visited[v] == FALSE ) bfs ( g , v ) ;
}

void bfs ( GraphMatrix* g , Vertex v ) {
Vertex v1 , v2;
PSeqQueue q = createEmptyQueue_seq(); /* 队列元素的类型为Vertex* */

enQueue_seq ( q, v ) ;
printf("%d ", v);
visited[v] = TRUE ;

while ( !isEmptyQueue_seq(q) ) {
v1 = frontQueue_seq ( q ) ;
deQueue_seq ( q );
v2 = firstAdjacent ( g, v1 );

while ( v2!= NON ) {
if ( visited[v2] == FALSE ) {
enQueue_seq ( q, v2 );
visited[v2] = TRUE ;
printf("%d ", v2);
}
v2 = nextAdjacent ( g, v1 , v2 ) ;
}
}
}

GraphMatrix graph = {
6,
{{0,10,MAX,MAX,19,21},
{10,0,5,6,MAX,11},
{MAX,5,0,6,MAX,MAX},
{MAX,6,6,0,18,14},
{19,MAX,MAX,18,0,33},
{21,11,MAX,14,33,0}
}
};

int main(){
bft(&graph);
return 0;
}



文章由 算法源码吧(https://www.sodocs.net/doc/6b10520556.html,)收集
/** Author: 潘乔木(panqiaomu)

** DepthFirstSearch.c
**/

基本理论:
1,用邻接表做存储便于寻找一个顶点的邻接点和下一个邻接点;
2,基于邻接表的图的遍历本质就是查找邻接点,由于通过边或者弧查找,那么查找时间为O(e),总的时间复杂度为O(n+e)。
3,采用递归思想。
#include "stdafx.h"
#include<stdio.h>
#include<stdlib.h>
#define MAX_VERTEX_NUM 10
typedef int ARC_INFO_TYPE; //弧相关信息类型
typedef char VERTEX_TYPE; //结点信息类型
typedef enum{UDG, DG}GRAPH_TYPE; //图的类型:无向,有向图
//弧结点(表结点)
typedef struct ArcNode{
int num; //结点编号
ARC_INFO_TYPE* info;//弧的其他相关信息,如权值等等
struct ArcNode* nextArcNode;//指向下一个表结点的指针
}ArcNode;
//顶点(头节点)
typedef struct VNode{
VERTEX_TYPE data;//顶点的数据
ArcNode* firstArcNode;//指向第一个弧结点的指针
}VNode, AdjList[MAX_VERTEX

_NUM];//邻接表
//整个图的信息
typedef struct ALGraph{
int vertexNum;//结点个数
int arcNum;//边或者弧个数
GRAPH_TYPE kind;
}ALGraph;
//定义全局变量,便于操作
ALGraph Graph;
AdjList adjList;
//辅助数组,记录每个顶点是否被访问过
int visited[MAX_VERTEX_NUM];
//创建一个图

int CreateALGraph(void){
int i;
int v1,v2;//暂时存放两个相关联点的编号
ArcNode* p=NULL;
printf("输入结点个数,边数,图类型:\n");
scanf("%d,%d,%d",&Graph.vertexNum,&Graph.arcNum,&Graph.kind);
fflush(stdin); //注意:清除缓冲区的残余输入流,这里清除回车后留下的'\r' and'\n'之一,否则下面的输入出错
printf("输入顶点的数据:\n");
for(i = 0; i < Graph.vertexNum; ++i){ //初始化各个顶点(头节点)
sca
nf("%c",&adjList[i].data);
adjList[i].firstArcNode = NULL;
}
printf("输入相关联的边:\n");
for(i = 0 ; i < Graph.arcNum; ++i){ //输入所有边(关联的点)编号
scanf("%d,%d",&v1,&v2);
p = (ArcNode*)malloc(sizeof(ArcNode));
p->num = v2; (按照有向图处理,所以不是v1)
p->nextArcNode = adjList[v1].firstArcNode;
adjList[v1].firstArcNode = p;
if(!Graph.kind){ //若为无向图(无向图在有向图的基础上多添加e个结点得到)
p = (ArcNode*)malloc(sizeof(ArcNode));
p->num = v1;
p->nextArcNode = adjList[v2].firstArcNode;
adjList[v2].firstArcNode = p;
}
}
return 1;
}
//查找顶点v的第一个邻接点
int FirstAdjVex(int v){
if(adjList[v].firstArcNode)
return adjList[v].firstArcNode->num;
else
return -1;//对于有向图中出度为0的顶点建立邻接表时不存在邻接点
}
//查找顶点v的下一个未被访问的邻接点
int NextAdjVex(int v, int lastV){
ArcNode* p;
p = adjList[v].firstArcNode;
while(p){
if(!visited[p->num] && p->num!=lastV)
return p->num;
else
p = p->nextArcNode;
}
if(!p)
return -1; //没有找到一个未被访问的邻接点
}
//对某一个节点作为始点的深度遍历算法
int DFS(int v){
int w;
visited[v] = 1; //已经被访问过
printf("%c",adjList[v].data);//输出顶点值
for(w = FirstAdjVer(v); w >= 0; w = NextAdjVer(v,w)) //for循环为了递归返回的时候访问那些没有被访问的邻接点
if(!visited[w])
DFS(w);
return 1;
}
//对整个图的深度遍历算法DFS
int DFSGraph(void){
int i;
//初始化辅助数组
for(i = 0; i < Graph.vertexNum; ++i)
visited[i] = 0;
//对每一个没有被访问的邻接点深度遍历
for(i = 0; i < Graph.vertexNum; ++i)
if(!visited[i])
DFS(i);
return 1;
}
//主函数测试
int main(void){
printf("创建以邻接表为存储结构的无向图........\n");
CreateALGraph();
printf("深度优先搜索遍历图:\n")

;
DFSGraph();
system("pause");
return 0;
}

//测试数据:

参考严老教材157页图7.1(b) 设v1,v2,v3,v4,v5编号分别为0,1,2,3,4,5,对应的data值为a,e,c,b,d。经过输入,得到正确的遍历结果:abcde。7.1(a)的有向图可以同样得到遍历结果:acdb.欢迎测试


上一篇:穷举搜索::回溯与深搜 && 禁忌搜...
下一篇:内排序算法的比较
相关文章:
? 邻接矩阵存储方式下,对图进行深... ? 深度优先搜索算法(回溯法)入门
? 邻接表存储方式下,对图进行深度... ? 图的深度优先搜索的非递归版本算...
? 分油问题再解(深度优先搜索算法C... ? 深度优先搜索算法之非递归实现及...
? [笔记]图的算法(一)宽度优先搜索... ? 深度优先搜索算法(DFS)
? 常用算法——深度优先搜索 ? 深度优先搜索算法Matlab源码 zz


相关主题