连接表的广度优先遍历
对《大话数据结构》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出队。
-------------------------------------------------
原创博客 转载请注明出处http://www.cnblogs.com/hslzju
-------------------------------------------------
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步