图的遍历算法Word格式文档下载.docx
《图的遍历算法Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《图的遍历算法Word格式文档下载.docx(17页珍藏版)》请在冰豆网上搜索。
![图的遍历算法Word格式文档下载.docx](https://file1.bdocx.com/fileroot1/2022-11/28/be4322a2-db11-40df-9c48-8733b9f4b936/be4322a2-db11-40df-9c48-8733b9f4b9361.gif)
//FirstAdjVex返回v的第一个邻接顶点,若顶点在G中没有邻接顶点,则返回空(0)。
//若w是v的邻接顶点,NextAdjVex返回v的(相对于w的)下一个邻接顶点。
//若w是v的最后一个邻接点,则返回空(0)。
visited[w])
DFS(G,w);
//对v的尚未访问的邻接顶点w调用DFS
由以上叙述可知,深度优先搜索算法的效率取决于图的数据结构的表示方法。
当访问某顶点Vi时,DFS的时间主要消耗在从该顶点出发搜索它的所有邻接点上。
用邻接矩阵表示图时,其搜索时间为O(n);
用邻接表表示图时,需搜索第i个边表上的所有结点。
因此,对所有n个顶点访问,在邻接矩阵上共需检查n2个矩阵元素,而在邻接表上需将边表中所有e个结点检查一遍。
故由理论分析可知,DFS的时间复杂度为O(n2)或O(n+e)。
1.2广度优先搜索算法
广度优先搜索算法是树的按层次遍历的推广,它的基本思路是:
首先访问初始点Vi,并将其标记为已访问点,然后访问Vi的所有未被访问过的邻接点Vi1,Vi2,…Vit,并均标记为已访问,再按照Vi1,Vi2,…Vit的顺序,依次访问每一个顶点的所有未被访问过的邻接点,并标记为已访问。
依次类推,直到图中所有和初始点Vi有路径相通的顶点都被访问过为止。
如果仍有未被访问过的顶点,该算法必须从图的其它连通分量的任意顶点重新开始。
其非递归算法如下:
voidBFSTraverse(GraphG,Status(*Visit)(intv)){
G.vexnum,++v)
initQueue(Q);
//置空辅助队列Q
visited[v]){
EnQueue(Q,v);
//v入队列
while(!
QueueEmpty(Q)){
DeQueue(Q,u);
//队头元素出队并置为u
for(w=FirstAdjVex(G,u);
w=NextAdjVex(G,u,w))
Visited[w]){//w为u的尚未访问的邻接顶点
Visited[w]=TRUE;
VisitFunc(w);
EnQueue(Q,w);
}}}}
广度优先搜索算法的时间复杂度和深度优先搜索算法的时间复杂度相同。
和深度优先搜索不同的是,广度优先搜索的队列是惟一的,对于具有n个顶点和e条边的无向图或有向图,每个顶点均入队一次,当图是连通图时,只需要调用一次邻接矩阵或邻接链表即可完成遍历操作。
故邻接矩阵表示法的遍历时间效率为O(n2),邻接链表表示法的遍历时间效率为O(n+e)。
1.3运行结果及分析
这里选择邻接矩阵作为图的存储结构编写程序,然后将图1的顶点和边输入程序作为测试。
图1测试图
程序运行结果如下图2:
图2运行结果图
由结果知,对图1深度优先遍历的结果为:
abdhecfg,广度优先遍历的结果为abcdefgh。
又因为程序的时间效率为O(n2),当测试数值非常小时,程序运行的时间将十分小,忽略不计,故遍历时间为0。
2.字符串匹配问题
字符串匹配(Stringmatch)是在实际工作中经常碰到的问题,通常是输入主字符串(String)和字串(又称模式Pattern)组成,然后根据一定的算法来得出字串在主字符串中的位置。
通常精确的字符串匹配算法包括暴力搜索(Bruteforce,又叫蛮力法),KMP(Knuth-Morris-Pratt),BM(BoyerMoore)等等。
假定原字符串长度为n,子字符串长度为m,下面将介绍以上这三种方法并给出其实现。
2.1蛮力法
蛮力法是一种简单的匹配算法,它将字串和主字符串从左方对齐,然后从左到右将子串和主字符串中每一对相应的字符串进行匹配,如果一旦不匹配,则把字串向右移动一格,再进行下一轮匹配。
因为这种尝试的最大次数是n-m+1次,在最坏的情况下,每次尝试需要进行m次比较,所以在最坏的情况下,字符比较的次数为m*(n-m+1)。
故蛮力法的时间效率为O(mn)。
其设计思想为:
①在串S、T中比较的起始下标为i和j;
②循环直到S中剩下的字符个数小于T的长度或T的所有
字符都比较完:
如果S[i]=T[j],则继续比较S和T的下一个字符,否则
将i和j回溯,进行下一趟比较;
③如果T中的字符都比较完,则匹配成功,返回匹配的起
始下标,否则匹配失败,返回0。
2.2KMP算法
KMP算法使用了输入增强的思想,对模式进行预处理以得到一些信息,把这些信息存储在表中,然后在给定文本中实际查找模式时使用这些信息。
KMP算法也是将子字符串从左到右和主串进行匹配,和蛮力法不同的是,KMP算法在匹配失败后,并不是简单的从目标串的下一个字符串开始新一轮的检测,而是依据在检测之前得到的有用信息,直接跳过不必要的检测,从主串中找一个和子串字符匹配成功的字符,以这个字符为起点将字串对齐,然后开始新的匹配。
从而达到一个较高的匹配效率。
KMP算法的时间复杂度为O(n+m),当m远小于n的时候,算法的效率将取决于主字符串的长度,即时间复杂度为O(n)。
①在串S、T中比较的起始下标为i和j;
②循环直到S中剩下的字符个数小于T的长度或T的所有字符都比较完:
如果S[i]=T[j],则继续比较S和T的下一个字符,否则将i向右滑动到next[j]的位置,即j=next[i];
如果j=0,则将i和j分别加1,准备进行下一趟比较。
③如果T中的字符都比较完,则匹配成功,返回匹配的起始下标,否则匹配失败,返回0。
2.3BM算法
BM算法是一种精确字符串匹配算法,采用输入增强思想,对模式进行预处理以得到一些有用信息。
和KMP算法不同的是,BM算法采用从右到左比较的方法,同时应用到了坏字符规则和好后缀规则,来决定向右跳跃的距离。
所谓坏字符规则就是在BM算法从右到左扫描过程中,若发现某个字符x不匹配,则按如下两种情况考虑:
A.如果字符x在模式p中没有出现,那么从字符x开始的m个文本显然不可能与p匹配成功,直接跳过该区域即可。
B.如果x在模式p中出现,则以该字符为起点将子串对齐。
好后缀规则为:
若发现某个字符不匹配的同时,已有部分字符匹配成功,则按如下情况考虑:
A.如果在模式p中位置t处已匹配部分p1在p中某位置t1也出现,且位置t1前一个字符与位置t的前一个字符不相同,则将p右移使t1对于t方才所在的位置。
B.如果在p中任何位置已匹配部分p1都没有再出现,则找到与p1的后缀p2相同的p的最长前缀x,向右移动p,使得x对应方才p2后缀所在位置。
BM算法的时间复杂度为O(n+m2),当子串长度m非常小时,算法效率取决与主字符串的长度n。
故其时间复杂度为O(n)。
2.运行结果及分析
根据上面的介绍,我们编写程序来实现三种字符串匹配算法。
如图3所示
图3匹配算法运行结果图
由图3我们可知当随机输入主字符串和子字符串时,BF算法运行时间为0.016秒,而另外两种算法的时间为0,可忽略不计。
可知明显蛮力法所花费的时间比较多,这和前面的理论分析中的蛮力法的时间复杂度为O(m*n)相符合。
而通过对结果分析知,KMP算法和BM算法的匹配效率非常高,这和理论分析中二者时间复杂度为O(n)也相符合。
事实上,当我们输入的字符串长度非常大时,这种时间上的优势将更加明显,这里就不再一一测试了。
附录(程序代码)
1.图的遍历
#include<
iostream>
#include"
time.h"
#defineINFINITY32767
#defineMAX_VEX20//最大顶点个数
#defineQUEUE_SIZE(MAX_VEX+1)//队列长度
usingnamespacestd;
bool*visited;
//图的邻接矩阵存储结构
typedefstruct{
char*vexs;
//顶点向量
intarcs[MAX_VEX][MAX_VEX];
//邻接矩阵
intvexnum,arcnum;
//图的当前顶点数和弧数
}Graph;
//队列类
classQueue{
public:
voidInitQueue(){
base=(int*)malloc(QUEUE_SIZE*sizeof(int));
front=rear=0;
}
voidEnQueue(inte){
base[rear]=e;
rear=(rear+1)%QUEUE_SIZE;
voidDeQueue(int&
e){
e=base[front];
front=(front+1)%QUEUE_SIZE;
int*base;
intfront;
intrear;
};
//图G中查找元素c的位置
intLocate(GraphG,charc){
for(inti=0;
i<
i++)
if(G.vexs[i]==c)returni;
return-1;
//创建无向网
voidCreateUDN(Graph&
G){
inti,j,w,s1,s2;
chara,b,temp;
printf("
输入顶点数和弧数:
"
);
scanf("
%d%d"
&
G.vexnum,&
G.arcnum);
temp=getchar();
//接收回车
G.vexs=(char*)malloc(G.vexnum*sizeof(char));
//分配顶点数目
输入%d个顶点.\n"
G.vexnum);
for(i=0;
i++){//初始化顶点
输入顶点%d:
i);
%c"
G.vexs[i]);
i++)//初始化邻接矩阵
for(j=0;
j<
j++)
G.arcs[i][j]=INFINITY;
输入%d条弧.\n"
G.arcnum);
G.arcnum;
i++){//初始化弧
输入弧%d:
%c%c%d"
a,&
b,&
w);
//输入一条边依附的顶点和权值
s1=Locate(G,a);
s2=Locate(G,b);
G.arcs[s1][s2]=G.arcs[s2][s1]=w;
//图G中顶点k的第一个邻接顶点
intFirstVex(GraphG,intk){
if(k>
=0&
&
k<
G.vexnum){//k合理
if(G.arcs[k][i]!
=INFINITY)returni;
//图G中顶点i的第j个邻接顶点的下一个邻接顶点
intNextVex(GraphG,inti,intj){
if(i>
i<
G.vexnum&
j>
j<
G.vexnum){//i,j合理
for(intk=j+1;
k<
k++)
if(G.arcs[i][k]!
=INFINITY)returnk;
//深度优先遍历
voidDFS(GraphG,intk){
inti;
if(k==-1){//第一次执行DFS时,k为-1
if(!
visited[i])DFS(G,i);
else{
visited[k]=true;
%c"
G.vexs[k]);
//访问第k个顶点
for(i=FirstVex(G,k);
i>
i=NextVex(G,k,i))
//对k的尚未访问的邻接顶点i递归调用DFS
//广度优先遍历
voidBFS(GraphG){
intk;
QueueQ;
//辅助队列Q
Q.InitQueue();
visited[i]){//i尚未访问
visited[i]=true;
G.vexs[i]);
Q.EnQueue(i);
//i入列
while(Q.front!
=Q.rear){
Q.DeQueue(k);
//队头元素出列并置为k
for(intw=FirstVex(G,k);
w>
w=NextVex(G,k,w))
visited[w]){//w为k的尚未访问的邻接顶点
visited[w]=true;
G.vexs[w]);
Q.EnQueue(w);
//主函数
intmain(){
time_tDFSstart,DFSend;
time_tBFSstart,BFSend;
doubletotaltime1,totaltime2;
GraphG;
CreateUDN(G);
visited=(bool*)malloc(G.vexnum*sizeof(bool));
\n深度优先遍历:
"
DFSstart=clock();
visited[i]=false;
DFS(G,-1);
DFSend=clock();
totaltime1=(double)(DFSend-DFSstart)/CLOCKS_PER_SEC;
\n深度优先遍历时间:
\n"
cout<
<
totaltime1<
endl;
\n广度优先遍历:
BFSstart=clock();
BFS(G);
BFSend=clock();
totaltime2=(double)(BFSend-BFSstart)/CLOCKS_PER_SEC;
\n广度优先遍历时间:
totaltime2<
\n程序结束.\n"
return0;
2.字符串匹配
string>
#include"
classstringcom
{
private:
chars[256],t[256];
//分别代表主串与要匹配的串
intnum,pos[256];
//分别记录匹配次数与所出现匹配的位置
intnext[256],dist[256];
//KMP,BM记录的不匹配时下一位置
FILE*fp1,*fp2;
public:
voidfile();
voidBF();
voidKMP();
voidBM();
voidGetNext();
voidGetDist();
voidInput()
{cin>
>
s>
t;
voidOutput();
voidmain()
stringcomstr;
请输入主串S,和子串t:
str.Input();
//这里选择输入字符串
str.BF();
str.KMP();
str.BM();
voidstringcom:
:
BF()//蛮力法匹配
inti,j=0;
time_tstart,end;
doubletotaltime;
num=0;
start=clock();
for(i=0;
strlen(s);
if(s[i]==t[j])
{
j++;
if(!
t[j])
{
pos[num]=i-j+2;
num++;
i=i-strlen(t)+1;
//i从下一个位置开始,避免了重叠的串
j=0;
}
}
else
i=i-j;
j=0;
endl<
按BF算法实现:
Output();
end=clock();
totaltime=(double)(end-start)/CLOCKS_PER_SEC;
BF算法运行时间为:
%.8f"
totaltime);
/***************KMP算法******************/
KMP()//输入增强
inti,j;
i=0;
j=0;
GetNext();
while(strlen(s)-i>
0)
if(j==0||(s[i]==t[j]))
i++;
j++;
pos[num]=i-strlen(t)+1;
//位置从1开始记录,下标从0开始
i=pos[num];
elsej=next[j];
按KMP算法实现:
KMP算法运行时间为:
/***********GetNext******************/
GetNext()
intj,k;
next[1]=0;
j=1;
k=0;
while(j<
strlen(t))
if(k==0||(t[j]==t[k]))
k++;
next[j]=k;
elsek=next[k];
/************BM算法**********************/
BM()
inti,j,len;
len=strlen(t);
i=len-1;
GetDist();
while(i<
strlen(s))
{
j=len-1;
while(j>
=0&
s[i]==t[j])
i--;
j--;
if(j==-1)
pos[num++]=i+2;
i+=strlen(t)+1;
elsei+=dist[s[i]];
按B