if(!
visited[v]){
visited[v]=TRUE;VisitFunc(v);
EnQueue(Q,v);//v入队列
while(!
QueueEmpty(Q)){
DeQueue(Q,u);//队头元素出队并置为u
for(w=FirstAdjVex(G,u);w>=0;w=NextAdjVex(G,u,w))
if(!
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
#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;
}
public:
int*base;
intfront;
intrear;
};
//图G中查找元素c的位置
intLocate(GraphG,charc){
for(inti=0;iif(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));//分配顶点数目
printf("输入%d个顶点.\n",G.vexnum);
for(i=0;iprintf("输入顶点%d:
",i);
scanf("%c",&G.vexs[i]);
temp=getchar();//接收回车
}
for(i=0;ifor(j=0;jG.arcs[i][j]=INFINITY;
printf("输入%d条弧.\n",G.arcnum);
for(i=0;iprintf("输入弧%d:
",i);
scanf("%c%c%d",&a,&b,&w);//输入一条边依附的顶点和权值
temp=getchar();//接收回车
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&&kfor(inti=0;iif(G.arcs[k][i]!
=INFINITY)returni;
}
return-1;
}
//图G中顶点i的第j个邻接顶点的下一个邻接顶点
intNextVex(GraphG,inti,intj){
if(i>=0&&i=0&&jfor(intk=j+1;kif(G.arcs[i][k]!
=INFINITY)returnk;
}
return-1;
}
//深度优先遍历
voidDFS(GraphG,intk){
inti;
if(k==-1){//第一次执行DFS时,k为-1
for(i=0;iif(!
visited[i])DFS(G,i);//对尚未访问的顶点调用DFS
}
else{
visited[k]=true;
printf("%c",G.vexs[k]);//访问第k个顶点
for(i=FirstVex(G,k);i>=0;i=NextVex(G,k,i))
if(!
visited[i])DFS(G,i);//对k的尚未访问的邻接顶点i递归调用DFS
}
}
//广度优先遍历
voidBFS(GraphG){
intk;
QueueQ;//辅助队列Q
Q.InitQueue();
for(inti=0;iif(!
visited[i]){//i尚未访问
visited[i]=true;
printf("%c",G.vexs[i]);
Q.EnQueue(i);//i入列
while(Q.front!
=Q.rear){
Q.DeQueue(k);//队头元素出列并置为k
for(intw=FirstVex(G,k);w>=0;w=NextVex(G,k,w))
if(!
visited[w]){//w为k的尚未访问的邻接顶点
visited[w]=true;
printf("%c",G.vexs[w]);
Q.EnQueue(w);
}
}
}
}
//主函数
intmain(){
inti;
time_tDFSstart,DFSend;
time_tBFSstart,BFSend;
doubletotaltime1,totaltime2;
GraphG;
CreateUDN(G);
visited=(bool*)malloc(G.vexnum*sizeof(bool));
printf("\n深度优先遍历:
");
DFSstart=clock();
for(i=0;ivisited[i]=false;
DFS(G,-1);
DFSend=clock();
totaltime1=(double)(DFSend-DFSstart)/CLOCKS_PER_SEC;
printf("\n深度优先遍历时间:
\n");
cout<printf("\n广度优先遍历:
");
BFSstart=clock();
for(i=0;ivisited[i]=false;
BFS(G);
BFSend=clock();
totaltime2=(double)(BFSend-BFSstart)/CLOCKS_PER_SEC;
printf("\n广度优先遍历时间:
\n");
cout<printf("\n程序结束.\n");
return0;
}
2.字符串匹配
#include
#include
#include"time.h"
usingnamespacestd;
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;
cout<<"请输入主串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;iif(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;
}
cout<"<Output();
end=clock();
totaltime=(double)(end-start)/CLOCKS_PER_SEC;
printf("BF算法运行时间为:
%.8f",totaltime);
}
/***************KMP算法******************/
voidstringcom:
:
KMP()//输入增强
{
inti,j;
time_tstart,end;
doubletotaltime;
num=0;
i=0;
j=0;
start=clock();
GetNext();
while(strlen(s)-i>0)
if(j==0||(s[i]==t[j]))
{
i++;j++;
if(!
t[j])
{
pos[num]=i-strlen(t)+1;//位置从1开始记录,下标从0开始
i=pos[num];//i从下一个位置开始,避免了重叠的串
num++;
j=0;
}
}
elsej=next[j];
cout<"<Output();
end=clock();
totaltime=(double)(end-start)/CLOCKS_PER_SEC;
printf("KMP算法运行时间为:
%.8f",totaltime);
}
/***********GetNext******************/
voidstringcom:
:
GetNext()
{
intj,k;
next[1]=0;
j=1;
k=0;
while(jif(k==0||(t[j]==t[k]))
{
j++;
k++;
next[j]=k;
}
elsek=next[k];
}
/************BM算法**********************/
voidstringcom:
:
BM()
{
inti,j,len;
time_tstart,end;
num=0;
len=strlen(t);
i=len-1;
doubletotaltime;
start=clock();
GetDist();
while(i{
j=len-1;
while(j>=0&&s[i]==t[j])
{
i--;
j--;
}
if(j==-1)
{
pos[num++]=i+2;
i+=strlen(t)+1;//i从下一个位置开始,避免了重叠的串
}
elsei+=dist[s[i]];
}
cout<