目录
图的表示:邻接表
邻接表结构原理
在邻接列表实现中,每一个顶点会存储一个从它这里开始的相邻边的列表。比如,如果顶点 B 有一条边到 A、C 和 E,那么 A 的列表中会有 3 条边。
邻接列表只描述指向外部的边。B 有一条边到 A,但是 A 没有边到 B,所以 B 没有出现在 A 的邻接列表中。
查找两个顶点之间的边或者权重会比较费时,因为遍历邻接列表直到找到为止。
![](https://i-blog.csdnimg.cn/blog_migrate/8095ab457b07518ff2e5e1fadbb71645.png)
邻接表的数据结构
typedef struct _EdgeNode//与结点连接的边
{
int adjvex;//邻接的顶点
int weight;//权重
struct _EdgeNode *next;//指向下一个顶点/边
}EdgeNode;
typedef struct _VertexNode//顶点结点
{
char data;//结点数据
struct _EdgeNode *first;
}VertexNode,*AdjList;
typedef struct _AdJListGraph
{
AdjList adjList;//顶点数组
int numVex;
int numEdg;
}AdjListGraph;
邻接表的初始化
bool Init(AdjListGraph &gh)
{
gh.adjList = new VertexNode[MAXSIZE];//分配顶点数组地址
if (!gh.adjList) return false;
gh.numEdg = 0;
gh.numVex = 0;
}
邻接表的创建
//寻找顶点的数据找到数组的下标
int Location(AdjListGraph gh, char c)
{
if (gh.numVex <= 0) return -1;
for (int i=0;i<gh.numVex;i++)
{
if (c==gh.adjList[i].data)
{
return i;
}
}
return-1;
}
//图的创建
void CreateALGraph(AdjListGraph &gh)
{
cout << "输入图的定点数 和边数:";
cin >> gh.numVex >> gh.numEdg;
if (gh.numVex > MAXSIZE) return;
cout << endl << "输入相关顶点: " << endl;
//保存顶点
for (int i=0;i<gh.numVex;i++)
{
cin >> gh.adjList[i].data;
gh.adjList[i].first = NULL;
}
char vi, vj;//保存输入的顶点;
int i, j;
cout << "请依次输入边(vi,vj)上的顶点序号:" << endl;
for(int k = 0; k < gh.numEdg; k++)
{
cin >> vi >> vj;
i = Location(gh, vi);
j = Location(gh, vj);
if (i>=0 && j>=0)
{
//头插法插入边
EdgeNode *temp = new EdgeNode;
temp->adjvex = j;
temp->next = gh.adjList[i].first;
gh.adjList[i].first = temp;
}
}
}
邻接表的深度遍历
1)深度优先遍历算法原理
❤️ 首先以一个未被访问过的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点;
❤️ 当没有未访问过的顶点时,则回到上一个顶点,继续试探别的顶点,直到所有的顶点都被访问过。
使用深度优先搜索来遍历这个图的具体过程是:
1. 首先从一个未走到过的顶点作为起始顶点,比如 A 顶点作为起点。2. 沿 A 顶点的边去尝试访问其它未走到过的顶点,首先发现 E 号顶点还没有走到过,于是访问 E 顶点。3. 再以 E 顶点作为出发点继续尝试访问其它未走到过的顶点,接下来访问 D 顶点。4. 再尝试以 D 顶点作为出发点继续尝试访问其它未走到过的顶点。5. 但是,此时沿 D 顶点的边,已经不能访问到其它未走到过的顶点,接下来返回到 E 顶点。6. 返回到 E 顶点后,发现沿 E 顶点的边也不能再访问到其它未走到过的顶点。此时又回到顶点 A( D-> E-> A),再以 A 顶点作为出发点继续访问其它未走到过的顶点,于是接下来访问 C 点。7. 以此类推8. 最终访问的结果是 A -> E -> D -> C -> B
2)深度优先遍历算法实现
bool visited[MAXSIZE] = {0};//全局数据用来判断元素是否被访问过
//对图上的顶点进行深度遍历
void DFS(AdjListGraph &gh,int i)
{
int nextNum = -1;
if (visited[i])//如果该结点已经被访问则返回
return;
//访问该结点
cout << gh.adjList[i].data << " ";
visited[i] = true;
EdgeNode *tmp = gh.adjList[i].first;
while (tmp)
{
nextNum = tmp->adjvex;
if (visited[nextNum]==false)
{
DFS(gh, nextNum);
}
tmp = tmp->next;
}
}
//对所有顶点进行深度遍历
void DFS_All(AdjListGraph &gh)
{
for (int i=0;i<gh.numVex;i++)
{
if (visited[i]==false)
{
DFS(gh, i);
}
}
}
邻接表的广度遍历
1)广度优先遍历算法原理
❤️ 首先以一个未被访问过的顶点作为起始顶点,访问其所有相邻的顶点;
❤️ 然后对每个相邻的顶点,再访问它们相邻的未被访问过的顶点,直到所有顶点都被访问过,遍历结束。
![](https://i-blog.csdnimg.cn/blog_migrate/3b3a195e5700a68dc3333afe78f70a37.png)
2)广度优先遍历算法实现
//对图上的顶点进行广度遍历
void BFS(AdjListGraph &gh,int i)
{
int cur = -1;
queue<int> q;
q.push(i);
while (!q.empty())//队列不为空
{
cur = q.front();//取队列的头元素
if (visited[cur]==false)
{
cout << gh.adjList[cur].data << " ";
visited[cur] = true;
}
q.pop();
//取当前结点相邻的结点入队
EdgeNode *tmp = gh.adjList[cur].first;
while (tmp!=NULL)
{
q.push(tmp->adjvex);
tmp = tmp->next;
}
}
}
//对所有顶点进行广度遍历
void BFS_All(AdjListGraph &gh)
{
for (int i = 0; i < gh.numVex; i++)
{
if (visited[i] == false)
{
BFS(gh, i);
}
}
}
算法验证
程序清单
//Author:See qq3492625357
//代码为本人手写,若有错误或不当之处,欢迎指正。
#include <iostream>
#include <queue>
#define MAXSIZE 1024
using namespace std;
typedef struct _EdgeNode//与结点连接的边
{
int adjvex;//邻接的顶点
int weight;//权重
struct _EdgeNode *next;//指向下一个顶点/边
}EdgeNode;
typedef struct _VertexNode//顶点结点
{
char data;//结点数据
struct _EdgeNode *first;
}VertexNode,*AdjList;
typedef struct _AdJListGraph
{
AdjList adjList;//顶点数组
int numVex;
int numEdg;
}AdjListGraph;
//图的初始化
bool Init(AdjListGraph &gh)
{
gh.adjList = new VertexNode[MAXSIZE];//分配顶点数组地址
if (!gh.adjList) return false;
gh.numEdg = 0;
gh.numVex = 0;
}
//寻找顶点的数据找到数组的下标
int Location(AdjListGraph gh, char c)
{
if (gh.numVex <= 0) return -1;
for (int i=0;i<gh.numVex;i++)
{
if (c==gh.adjList[i].data)
{
return i;
}
}
return-1;
}
//图的创建
void CreateALGraph(AdjListGraph &gh)
{
cout << "输入图的定点数 和边数:";
cin >> gh.numVex >> gh.numEdg;
if (gh.numVex > MAXSIZE) return;
cout << endl << "输入相关顶点: " << endl;
//保存顶点
for (int i=0;i<gh.numVex;i++)
{
cin >> gh.adjList[i].data;
gh.adjList[i].first = NULL;
}
char vi, vj;//保存输入的顶点;
int i, j;
cout << "请依次输入边(vi,vj)上的顶点序号:" << endl;
for(int k = 0; k < gh.numEdg; k++)
{
cin >> vi >> vj;
i = Location(gh, vi);
j = Location(gh, vj);
if (i>=0 && j>=0)
{
EdgeNode *temp = new EdgeNode;
temp->adjvex = j;
temp->next = gh.adjList[i].first;
gh.adjList[i].first = temp;
}
}
}
bool visited[MAXSIZE] = {0};//全局数据用来判断元素是否被访问过
//对图上的顶点进行深度遍历
void DFS(AdjListGraph &gh,int i)
{
int nextNum = -1;
if (visited[i])//如果该结点已经被访问则返回
return;
//访问该结点
cout << gh.adjList[i].data << " ";
visited[i] = true;
EdgeNode *tmp = gh.adjList[i].first;
while (tmp)
{
nextNum = tmp->adjvex;
if (visited[nextNum]==false)
{
DFS(gh, nextNum);
}
tmp = tmp->next;
}
}
//对所有顶点进行深度遍历
void DFS_All(AdjListGraph &gh)
{
for (int i=0;i<gh.numVex;i++)
{
if (visited[i]==false)
{
DFS(gh, i);
}
}
}
//对图上的顶点进行广度遍历
void BFS(AdjListGraph &gh,int i)
{
int cur = -1;
queue<int> q;
q.push(i);
while (!q.empty())//队列不为空
{
cur = q.front();//取队列的头元素
if (visited[cur]==false)
{
cout << gh.adjList[cur].data << " ";
visited[cur] = true;
}
q.pop();
//取当前结点相邻的结点入队
EdgeNode *tmp = gh.adjList[cur].first;
while (tmp!=NULL)
{
q.push(tmp->adjvex);
tmp = tmp->next;
}
}
}
//对所有顶点进行广度遍历
void BFS_All(AdjListGraph &gh)
{
for (int i = 0; i < gh.numVex; i++)
{
if (visited[i] == false)
{
BFS(gh, i);
}
}
}
int main()
{
AdjListGraph G;
cout << "正在创建邻接表,请按提示进行输入..." << endl;
Init(G);
CreateALGraph(G);
cout << "正在进行深度优先遍历,遍历结果如下:" << endl;
//深度优先遍历
DFS_All(G);
cout << endl;
memset(visited, 0, sizeof(visited));
cout << "正在进行广度优先遍历,遍历结果如下:" << endl;
//广度优先遍历
BFS_All(G);
cout << endl;
}