最近为了保研在复习数据结构和算法,想来可以用博客记录一些,以后或许能用的上。
首先说一下图的定义。
图是一种数据结构,图和树一样可以用二元组表示。它可定义为Graph=(V,R)其中,V={x|x∈datatype},R={VR},VR={(x,y)|P(x,y)∧(x,y∈V)}。在图中,数据元素常称为顶点(Vertex),V是顶点的非空有穷集合;R是边(弧)的有穷集合。
也就是说,图就是记录一些点和点之间的关系的数据结构。
图的存储大致分为两类,因为主要想说的是遍历,所以大概说一下存储。
- 邻接矩阵
邻接矩阵就是一个二维数组,不同的下标对应着不同的结点。而如果是无向图,vi<->vj 即 graph[vi][vj] = graph[vj][vi] = 1; 有向图 vi->vj 则只有graph[vi][vj] = 1。如果边上有权重val,则graph[vi][vj] = val。
而虽然邻接矩阵在记录边关系时很方便,但是面临的问题是浪费大量空间,特别是面对稀疏图(即图中边的数量大大少于节点数量)时,问题尤其明显。因为我们的示例图很简单,所以接下来我用的都是邻接矩阵。 - 邻接表
邻接表的出现解决了邻接矩阵的问题,即使用了链式结构。 邻接表的处理方法是这样的:
- 图中顶点用一个一维数组存储。
- 图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以,用链表存储。链表中每个节点有两个信息:当前顶点的下标,指向下一节点的指针。在该图中,数组里面的下标对应不同的顶点,而链表里的节点对应这个顶点的邻接点。
接下来是本文的主要点:图的遍历。
上面是一个无向图的示例图。
- 深度优先搜索(DFS)
由这个名字我们就能知道,DFS注重的是深度优先。即走一条路走到底,走不动了回来看有没有岔路口,有的话接着走到底。当然已经走过的地方不能再走一遍了。拿上图举例,我们从v1出发,如果我们先走左边的顶点(这个左边在程序里即第一个顶点),我们走到了v2,发现还能往下(下的意思是有邻接点存在)走,走左边到了v4,发现有岔路口,但是v1我们走过了,只能走v5,v5走到了v3。此时全部点都遍历了一遍,即DFS完成。顺序:v1,v2,v4,v5,v3。 - 广度优先搜索(BFS)
BFS注重的是广度,按层次遍历。即先把眼前的地方先走一遍,再接着遍历更远的地方。拿上图举例。我们从v1出发,首先把眼前的顶点遍历完(即遍历v1所有的邻接点),我们遍历了v2,v3,v4。然后从最左边的顶点接着重复此过程,遍历了v5。此时全部点都遍历了一遍,即BFS完成。顺序:v1,v2,v3,v4,v5。
我们可以看出来,BFS和DFS的时间复杂度相同,不同的只有遍历顶点的顺序不同而已。所以我们根据不同的情况使用不同的遍历方法即可。
下面是创建邻接矩阵图,DFS和BFS的代码:
#include<iostream>
#include<string.h>
#include<queue>
using namespace std;
#define MAX 100
#define max(x,y)(x>y?x:y)
#define initNode 1 //这里我们假设遍历从顶点1开始,也可以改成其他的
typedef struct{
int matrix[MAX][MAX]; //邻接矩阵
int num;// 最大的顶点数
}Graph;
Graph example; //实例图
// 创建图
void createPoint(){
int i,j;
memset(example.matrix,0,sizeof(example.matrix));
example.num=0;
while(cin>>i>>j&&i!=0 && j!=0){
example.matrix[i][j]=1;
example.matrix[j][i]=1;
example.num = max(example.num,i);
example.num = max(example.num,j);
}
}
// DFS 函数主体 用递归实现
void DFS(int x,int * visited){
cout<<" "<<x;
for(int i=1;i<=example.num;i++){
if(visited[i]==0 && example.matrix[x][i]==1){
visited[i]=1;
DFS(i,visited);
}
}
}
// DFS DFS初始化
void initDFS(){
int *visited = new int[example.num++];
memset(visited,0,sizeof(int)*(example.num+1));
cout<<"DFS"<<"------------------------------------"<<endl;
visited[initNode]=1;
DFS(initNode,visited);
cout<<endl<<"DFS"<<"------------------------------------"<<endl;
}
// BFS 用队列实现该功能
void BFS(){
int i,nNode;
queue<int> gQueue;
int *visited = new int[example.num++];
memset(visited,0,sizeof(int)*(example.num+1));
cout<<"BFS"<<"------------------------------------"<<endl;
visited[initNode]=1;
gQueue.push(initNode);
while(!gQueue.empty()){
nNode = gQueue.front();
cout<<" "<<nNode;
for(i=1;i<=example.num;i++){
if(example.matrix[nNode][i]==1 && visited[i]==0){
gQueue.push(i);
visited[i]=1;
}
}
gQueue.pop();
}
cout<<endl<<"BFS"<<"------------------------------------"<<endl;
}
int main(){
createPoint();
BFS();
initDFS();
return 0;
}
输出结果(输入的形式是以邻接点对的形式,以0 0结束):
1 2
1 4
1 3
2 4
2 5
3 5
4 5
0 0
BFS------------------------------------
1 2 3 4 5
BFS------------------------------------
DFS------------------------------------
1 2 4 5 3
DFS------------------------------------