您的位置:首页 > 其它

图:存储结构、深度优先遍历以及广度优先遍历

2017-04-09 16:00 726 查看
图:记录关系、联系,由数个点Vertex、数条边Edge组成。

同构 Isomorphism 英[ˌaɪsəʊ’mɔ:fɪzəm] :两张图的连接方式一样。

图的存储结构:

阵列

记录所有的点与所有的边。直观、节省空间、但不适于计算。



如图,记录(0,1) (0,3) (0,4)

邻接矩阵 adjacency matrix





无向图的边数组是个对称矩阵(aij=aji)。

顶点vi的度:vi所在行的元素之和

有向图讲究入度和出度。顶点vi的入度是vi所在列的元素之和,出度是所在行的元素之和

矩阵的值也可以是边的权重。

typedef struct{
VertexType vexes[MAX]; //顶点表
EdgeType arc[MAX][MAX]; //邻接矩阵
int numVertexes,numEdges; //顶点数,边数
} MGraph;


邻接表 adjacency lists

把一張圖上的點依序標示編號。每一個點,後方串連所有相鄰的點



typedef char VertexType;   //顶点类型
typedef int EdgeType;  //边上的权值类型

typedef struct EdgeNode{    //边表结点
int adjvex;
EdgeType weight;
struct EdgeNode *next;
} EdgeNode;

typedef struct VertexNode{    //顶点表结点
VertexType data;
EdgeNode *firstedge;
} VertexNode, AdjList[MAX];

typedef struct{
AdjList adjList;
int numVertexes,numEdges;
} GraphAdjList;


图的遍历:

两种遍历算法:深度优先遍历 Depth First Search,广度优先遍历 Breadth First Search,。

深度优先遍历

从图中某个顶点出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直到图中所有和v有路径相同的顶点都被访问到。

以下图为例。从A点出发,做上表示走过的记号。我们给自己定一个原则,在没有碰到重复顶点的情况下,始终往右手边走。于是走向B,接着C、D、E、F。继续往右手边走,发现A点已经访问过了,此时退回F点,走向从右数的第二个通道到达顶点G,G有三条通道,B和D都走过,所以走向H,H的两条通道D和E都走过。

此时是否已经遍历了所有顶点呢?没有。所以按原路返回,回到G,没有未走过的通道,回到F,没有未走过的通道,回到E,有一条通往H,验证后也是走过的。回到D,有三条道未走过,一条条来。走到I,这是个未标记过的顶点,标记下来。继续返回直到返回顶点A,确认完成遍历任务,找到了所有的9个顶点。





将这个过程化为上图。这其实就是一个递归的过程。这里以连通图为例,如果是非连通图,需要对它的连通分量分别进行深度优先遍历。

图的存储方式是邻接矩阵:

typedef int Boolean;
Boolean visited[MAX];
void DFS(MGraph G,int i){
int j;
visited[i]=TRUE;
printf("%c ",G.vexs[i]); //打印顶点,也可以进行其他操作
for(j=0;j<G.numVertexes;j++){
if(G.arc[i][j]==1 && !visited[j]) DFS(G,j); //对未访问的邻接顶点递归调用
}
}

void DFSTraverse(MGraph G){
int i;
for(i=0;i<G.numVertexes;i++){
visited[i]=FALSE;   //初始化所有顶点状态为未访问
}
for(i=0;i<G.numVertexes;i++){
if(!visited[i]){   //对未访问过的顶点调用DFS,如果是连通图,只会执行一次
DFS(G,i);
}
}
}


图的存储方式是邻接表

void DFS(GraphAdjList GL, int i){
EdgeNode *p;
visited[i]=TRUE;
printf("%c ",GL->adjList[i].data);
p=GL->adjList[i].firstedge;
while(p!=NULL){
if(!visited[p->adjvex]){
DFS(GL,p->adjvex);
}
p=p->next;
}
}
void DFSTraverse(GraphAdjList GL){
int i;
for(i=0;i<GL->numVertexes;i++){
visited[i]=FALSE;   //初始化所有顶点状态为未访问
}
for(i=0;i<GL->numVertexes;i++){
if(!visited[i]){   //对未访问过的顶点调用DFS,如果是连通图,只会执行一次
DFS(G,i);
}
}
}


对于n个顶点e条边的图来说,邻接矩阵由于是二维数组,要查找每个顶点的邻接点需要访问矩阵中的所有元素,因此都需要O(n2)的时间。

而邻接表做存储结构时,找邻接点所需时间取决于顶点和边的数量,所以是O(n+e),显然对于点多边少的稀疏图,邻接表结构使得算法在时间效率上大大提高。

广度优先遍历

(依照編號順序)不斷找出尚未遍歷的點當作起點,進行下述行為:

 一、把起點放入queue。

 二、重複下述兩步驟,直到queue裡面沒有東西為止:

  甲、從queue當中取出一點。

  乙、找出跟此點相鄰的點,並且尚未遍歷的點,

    通通(依照編號順序)放入queue。

只觀察離開 queue 的時刻,可以發現 BFS 優先走遍距離起點最近之處,優先讓 BFS Tree 變得寬廣,因而得名 Breadth-first Search 。這個遍歷順序能夠解決許多圖論問題!

时间复杂度:使用的資料結構為 adjacency matrix 的話,可以以 O(V^2) 時間遍歷整張圖;使用 adjacency lists 的話,可以以 O(V+E) 時間遍歷整張圖。 V 是圖上的點數, E 是圖上的邊數。

bool adj[9][9]; // 一張圖,資料結構為adjacency matrix。
bool visit[9];  // 記錄圖上的點是否遍歷過,有遍歷過為true。

void BFS()
{
// 建立一個queue。
queue<int> q;
// 全部的點都初始化為尚未遍歷
for (int i=0; i<9; i++)
visit[i] = false;
for (int k=0; k<9; k++)
if (!visit[k])
{
一、把起點放入queue。
q.push(k);
visit[k] = true;
// 二、重複下述兩點,直到queue裡面沒有東西為止:
while (!q.empty())
{
// 甲、從queue當中取出一點。
int i = q.front(); q.pop();
// 乙、找出跟此點相鄰的點,並且尚未遍歷的點,
// 依照編號順序通通放入queue。
for (int j=0; j<9; j++)
if (adj[i][j] && !visit[j])
{
q.push(j);
visit[j] = true;
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐