连接表的广度优先遍历

对《大话数据结构》P244——邻接表的广度优先遍历,进行了自己的理解并完善了代码。

举个简单的例子:

首先用邻接表的存储结构创建该图,再进行广度优先遍历。

代码和解释如下(VS2012测试通过):

  1 #include <iostream>
  2 #include <stdlib.h>
  3 using namespace std;
  4 #define MAXVERTEX 6//定义顶点数最多为6
  5 #define MAXSIZE 6//循环队列的最大长度定义为6
  6 
  7 //边表节点
  8 typedef struct EdgeNode
  9 {
 10     int adjvex;//存储该顶点对应的下标
 11     struct EdgeNode *next;//指向该顶点的下一个邻接点
 12 }EdgeNode;
 13 
 14 //顶点表结点
 15 typedef struct VertexNode
 16 {
 17     char data;//顶点
 18     EdgeNode *firstedge;//边表头指针
 19 }VertexNode;
 20 
 21 //图的邻接表存储结构
 22 typedef struct
 23 {
 24     VertexNode adjList[MAXVERTEX];//有6个VertexNode这种类型的顶点,定义一个数组adjList[6],每个元素是VertexNode类型
 25     int numVertexes,numEdges;//图中顶点数和边数,这里是6,9
 26 }GraphAdjList;
 27 
 28 //无向图的邻接表创建
 29 GraphAdjList *CreateALGraph(GraphAdjList *Gp)
 30 {
 31     Gp=(GraphAdjList *)malloc(sizeof(GraphAdjList));//申请一片GraphAdjList大小的类型很重要,否则Gp指向NULL(GL传的值是NULL),程序就运行崩溃了
 32     EdgeNode *pe;//定义边表指针类型pe
 33     cout << "input numNodes and numEdges:" << endl;
 34     cin >> Gp->numVertexes >> Gp->numEdges;//输入6 9
 35     for (int k = 0 ; k < Gp->numVertexes; k++)
 36     {
 37         cout << "input VertexType data:" << endl;
 38         cin >> Gp->adjList[k].data;//输入A B C D E F
 39         Gp->adjList[k].firstedge = NULL;//将边表头指针指向NULL,即置为0
 40     }
 41     for (int k = 0; k < Gp->numEdges; k++)//建立边表
 42     {
 43         int i,j;
 44         cout << "input vi and vj:" << endl;
 45         cin >> i >> j;//每次循环依次输入0 1,0 3,0 5,1 2,1 3,1 5,2 3,3 4,4 5
 46 
 47         pe = (EdgeNode *)malloc(sizeof(EdgeNode));
 48         pe->adjvex = j;// 邻接序号为j
 49         pe->next = Gp->adjList[i].firstedge;//将pe的指针指向当前顶点指向的结点
 50         Gp->adjList[i].firstedge = pe;//将当前顶点的指针指向pe
 51 
 52         pe = (EdgeNode *)malloc(sizeof(EdgeNode));
 53         pe->adjvex = i;
 54         pe->next = Gp->adjList[j].firstedge;
 55         Gp->adjList[j].firstedge = pe;//无序图重复上面步骤
 56     }
 57     return Gp;
 58 }
 59 
 60 //循环队列的顺序存储结构
 61 //保留一个元素空间,否则front==rear,是空队列还是满队列不容易判断
 62 typedef struct
 63 {
 64     char data[MAXSIZE];//队列,用数组形式表示  
 65     int front;//头序号,第一个元素的下标
 66     int rear;//尾序号,最后一个元素的下标
 67 }SqQueue;
 68 
 69 //循环队列的初始化,返回指向循环队列的地址
 70 SqQueue *InitQueue(SqQueue *Q)
 71 {
 72     Q=new SqQueue;
 73     Q->front=0;
 74     Q->rear=0;//初始化为空队列(front==rear为空队列)
 75     return Q;
 76 }
 77 
 78 //求循环队列的长度,返回循环队列的当前长度
 79 int QueueLength(SqQueue *Q)
 80 {
 81     return (Q->rear-Q->front+MAXSIZE)%MAXSIZE;
 82 }
 83 
 84 //循环队列的入队列操作
 85 void EnQueue(SqQueue *Q,char e)
 86 {
 87     if((Q->rear+1)%MAXSIZE==Q->front)//判断循环队列是否满
 88     {
 89         cout<<"error"<<" ";
 90         return;
 91     }
 92     Q->data[Q->rear]=e;//将元素e插入队尾
 93     Q->rear=(Q->rear+1)%MAXSIZE;//尾序号rear加1,若到最后转向头部
 94     cout<<e<<"in"<<" ";
 95     return;
 96 }
 97 
 98 //循环队列的出队列操作
 99 char DeQueue(SqQueue *Q)
100 {
101     char e;
102     if(Q->front==Q->rear)//如果队列是否空
103     {
104         cout<<"error"<<" ";
105         return 0;//返回0表示队列空,没有出队元素
106     }
107     e=Q->data[Q->front];
108     Q->front=(Q->front+1)%MAXSIZE;//头序号front加1,若到最后转向头部
109     cout<<e<<"out"<<" ";
110     return e;
111 }
112 
113 //邻接表的广度优先遍历
114 void BFSTraverse(GraphAdjList *G)
115 {
116     EdgeNode *p;//定义一个指向边表结点的指针
117     int visited[MAXVERTEX];//标志顶点是否被访问,0是未被访问过,1是被访问过
118     char e;
119     
120     SqQueue *s=NULL; 
121     s=InitQueue(s);//初始化一个循环队列s,最大长度为MAXSIZE=6,初始为空
122 
123     for(int i=0;i<G->numVertexes;i++)//初始化所有顶点都未被访问过
124         visited[i]=0;
125 
126     for(int i=0;i<G->numVertexes;i++)//对于连通图,执行一次就可以遍历所有顶点
127     {
128         if(!visited[i])//如果没有被访问过,就处理,防止不必要的重复处理
129         {
130             visited[i]=1;//设置当前顶点访问过
131             cout<<"print1"<<G->adjList[i].data<<" ";//打印该顶点
132             EnQueue(s,G->adjList[i].data);//把该顶点入队
133             while(QueueLength(s))//若当前队列长度不等于0(即不为空)
134             {
135                 e=DeQueue(s);//元素出队,需要知道出队元素的下标,更新i
136                 switch (e)                  
137                 {
138                     case 'A':i=0;break;
139                     case 'B':i=1;break;
140                     case 'C':i=2;break;
141                     case 'D':i=3;break;
142                     case 'E':i=4;break;
143                     case 'F':i=5;break;
144                     default:break;
145                 }
146                 cout<<endl;
147                 p=G->adjList[i].firstedge;//把刚才出队元素所在的顶点表结点的firstedge的指向给p,即找到与出队元素相连的另一个顶点
148                 while(p)
149                 {
150                     if(!visited[p->adjvex])//如果找到的这个顶点还没被访问过
151                     {
152                         visited[p->adjvex]=1;
153                         cout<<"print2"<<G->adjList[p->adjvex].data<<" ";//打印这个顶点
154                         EnQueue(s,G->adjList[p->adjvex].data);//把该顶点入队
155                     }
156                     p=p->next;//让p指向下一个边表节点
157                 }
158             }
159         }
160     }
161 }
162 
163 int main(void)
164 {
165     GraphAdjList *p=NULL;
166     p=CreateALGraph(p);//创建图
167     BFSTraverse(p);//广度遍历 
168 }

运行结果:

结果解释:

print1和print2用来标记在函数哪个部分打印,可以看出遍历顺序是AFDBEC。

也可以看出入队和出队的顺序。

打印A,A入队,A出队。

打印F,F入队;打印D,D入队;打印B,B入队。F出队。

打印E,E入队。D出队。

打印C,C入队。B出队。

E出队。

C出队。

posted @ 2016-04-19 20:40  Pearl_zju  阅读(444)  评论(2编辑  收藏  举报