一、图
一、临接表
表示方法如下:
带权值的无向图的构建:
#define MaxInt 32767 // 极大值
#define MVNum 100 // 最大定点数
typedef int ArcType; // 边的权值类型
typedef char VerTexType; // 顶点数据类型
//弧(边)的结点结构
struct ArcNode
{
int adjvex; // 该边指向顶点的下标
struct ArcNode *nextarc; // 下一条边指针
ArcType info; // 边的权值
};
// 顶点的结点结构
typedef struct VNode
{
VerTexType data; // 顶点信息
struct ArcNode *firstarc; // 指向第一条边关联该点的边
} VNode, AdjList[MVNum];
// 图的结构定义
typedef struct
{
AdjList vertices;
int vexnum, arcnum; // 图的顶点数和边数
} ALGraph;
创建图:
// 采用邻接表表示法创建无向网
Status CreateUDG(ALGraph &G)
{
cin >> G.vexnum >> G.arcnum; // 输入总顶点数与总边数
for (int i = 0; i < G.vexnum; ++i)
{
cin >> G.vertices[i].data;
G.vertices[i].firstarc = NULL;
}
for (int k = 0; k < G.arcnum; ++k)
{
char v1, v2;
cin >> v1 >> v2;
int i = LocateVex(G, v1);
int j = LocateVex(G, v2);
ArcNode *p1 = new ArcNode;
p1->adjvex = j;
p1->nextarc = G.vertices[i].firstarc;
G.vertices[i].firstarc = p1;
ArcNode *p2 = new ArcNode;
p2->adjvex = i;
p2->nextarc = G.vertices[j].firstarc;
G.vertices[j].firstarc = p2;
}
return 0;
}
//顶点在顶点表中的下标
int LocateVex(ALGraph G, VerTexType u)
{
int i;
for (int i = 0; i < G.vexnum; ++i)
if (u == G.vertices[i].data) return i;
return -1;
}
二、临接矩阵
表示如下:
带权值的有向图的构建:
#include <bits/stdc++.h>
using namespace std;
#define MaxVertices 100 //假设包含的最大结点数
#define MaxWeight -1 //假设两点不邻接的正无穷值
//定义结点
struct AdjMarix {
int Vertices[MaxVertices]; //存储结点信息
int Edge[MaxVertices][MaxVertices] = { 0 }; //存储每条边的权值
int numV; //当前顶点的个数
int numE; //当前边的个数
};
void CreatGraph(AdjMarix *G) {
int vi, vj, w;
cout << "请输入顶点数量:" << endl;
cin >> G->numV;
cout << "请输入顶点信息:" << endl;
//输入结点的编号并初始化
for (int i = 0; i < G->numV; i++) {
cin >> vi;
G->Vertices[i] = vi;
G->Edge[i][i] = MaxWeight; //初始化过程先默认权值为无穷大
}
cout << "请输入边的数量:" << endl;
cin >> G->numE;
cout << "请输入边的信息:" << endl;
for (int i = 0; i < G->numE; i++) {
cin >> vi >> vj >> w; //vi、vj为邻接矩阵对应点的坐标,w为边的权值
G->Edge[vi - 1][vj - 1] = w;
//G->Edge[vj-1][vi-1]=w; 无向图需要再加上这一句
}
}
//遍历图,展示邻接表矩阵
void ShowGraph(AdjMarix *G) {
for (int i = 0; i < G->numV; i++) {
for (int j = 0; j < G->numV; j++) {
cout << G->Edge[i][j] << " ";
}
cout << endl;
}
}
int main() {
AdjMarix AM;
CreatGraph(&AM);
ShowGraph(&AM);
}
输入输出的结果为:
三、图的宽度优先遍历
1,利用队列实现
2,从源节点开始依次按照宽度进队列,然后弹出
3,每弹出一个点,把该节点所有没有进过队列的邻接点放入队列
4,直到队列变空
邻接矩阵版:
#include <iostream>
#include <queue>
using namespace std;
#define MaxVertexNum 100 //顶点数目的最大值
// VertexType,顶点的数据类型
template<typename VertexType>
class MGraph {
private:
VertexType Vex[MaxVertexNum]; //顶点表
int Edge[MaxVertexNum][MaxVertexNum]; //邻接矩阵,边表
int vexnum, arcnum; //图的当前顶点数和弧数
bool inq[MaxVertexNum]; //如果顶点i已入过,inq[i]==true。初值为false
void BFS(int u) { //遍历u所在的连通块
queue<int> q; //定义队列q
q.push(u); //初始顶点u入队
inq[u] = true; //设置u已入过队
while (!q.empty()) { //只要队列非空
u = q.front(); //取出队首元素并访问
cout << Vex[u] << "\t";
q.pop(); //队首元素出队
for (int v = 0; v < vexnum; v++)
//如果u的邻接点v未曾加入过队列
if (Edge[u][v] == 1 && inq[v] == false) {
//将v入队并标记已入队
q.push(v);
inq[v] = true;
}
}
}
public:
MGraph() {
for (int i = 0; i < MaxVertexNum; i++) {
inq[i] = false;
for (int k = 0; k < MaxVertexNum; k++)
Edge[i][k] = 0;
}
}
void create() {
int row, col;
cin >> vexnum >> arcnum; //输入实际图的顶点数和边数
for (int i = 0; i < vexnum; i++) //输入顶点信息
cin >> Vex[i];
for (int i = 0; i < arcnum; i++) { //输入边信息
cin >> row >> col;
Edge[row][col] = 1;
}
}
void BFSTrave() { //遍历图G
for (int u = 0; u < vexnum; u++) //枚举所有顶点
if (inq[u] == false)
BFS(u); //遍历u所在的连通块
}
};
int main() {
MGraph<string> G;
G.create();
G.BFSTrave();
return 0;
}
邻接表版:
#include <iostream>
#include <queue>
using namespace std;
#define MaxVertexNum 100 //顶点数目的最大值
struct ArcNode { //边表结点
int adjvex; //该弧所指向的顶点的位置
ArcNode *next; //指向下一条弧的指针
};
template<typename VertexType>
struct VNode { //顶点表结点
VertexType data; //顶点信息
ArcNode *first; //指向第一条依附该顶点的弧的指针
};
template<typename VertexType>
class ALGraph { //ALGraph是以邻接表存储的图类型
private:
VNode<VertexType> vertices[MaxVertexNum]; //邻接表
int vexnum, arcnum; //图的顶点数和弧数
bool inq[MaxVertexNum]; //如果顶点i已入过,inq[i]==true。初值为false
void BFS(int u) { //遍历u所在的连通块
queue<int> q; //定义队列q
q.push(u); //初始顶点u入队
inq[u] = true; //设置u已入过队
while (!q.empty()) {
u = q.front(); //取出队首元素并访问
cout << vertices[u].data << "\t";
q.pop(); //队首元素出队
ArcNode *p = vertices[u].first;
while (p) {
if (inq[p->adjvex] == false) {
q.push(p->adjvex);
inq[p->adjvex] = true;
}
p = p->next;
}
}
}
public:
ALGraph() {
for (int i = 0; i < MaxVertexNum; i++) {
inq[i] = false;
vertices[i].first = NULL;
}
}
void create() {
int row, col;
cin >> vexnum >> arcnum; //输入实际图的顶点数和边数
for (int i = 0; i < vexnum; i++) //输入顶点信息
cin >> vertices[i].data;
for (int i = 0; i < arcnum; i++) { //输入边信息
cin >> row >> col;
ArcNode *p = new ArcNode;
p->adjvex = col;
p->next = vertices[row].first;
vertices[row].first = p;
}
}
void BFSTrave() { //遍历图G
for (int u = 0; u < vexnum; u++) //枚举所有顶点
if (inq[u] == false)
BFS(u); //遍历u所在的连通块
}
};
int main() {
ALGraph<string> G;
G.create();
G.BFSTrave();
return 0;
}
四、广度优先遍历
深度优先搜索以“深度”作为第一关键词,每次都是沿着路径到不能再前进时才退回到最近的岔道口。以一个有向图(见下图)进行 DFS 遍历来举例(从V0 开始进行遍历,黑色表示结点未访问,白色表示结点已访问,虚线边表示当前遍历路径):
如果要遍历整个图,就需要对所有连通块(连通分量和强连通分量)分别进行遍历。所以 DFS 遍历图的基本思路就是将经过的顶点设置为已访问,在下次递归碰到这个顶点时就不再去处理,直到整个图的顶点都被标记为已访问。
#include <iostream>
using namespace std;
#define MaxVertexNum 100 //顶点数目的最大值
struct ArcNode { //边表结点
int adjvex; //该弧所指向的顶点的位置
ArcNode *next; //指向下一条弧的指针
};
template<typename VertexType>
struct VNode { //顶点表结点
VertexType data; //顶点信息
ArcNode *first; //指向第一条依附该顶点的弧的指针
};
template<typename VertexType>
class ALGraph { //ALGraph是以邻接表存储的图类型
private:
VNode<VertexType> vertices[MaxVertexNum]; //邻接表
int vexnum, arcnum; //图的顶点数和弧数
bool visited[MaxVertexNum]; //如果顶点i已被访问,则visited[i]==true。初值为false
void DFS(int u) { //u为当前访问的顶点索引
cout << vertices[u].data << "\t";
visited[u] = true; //设置u已被访问
ArcNode *p = vertices[u].first;
while (p) {
if (visited[p->adjvex] == false)
DFS(p->adjvex);
p = p->next;
}
}
public:
ALGraph() {
for (int i = 0; i < MaxVertexNum; i++) {
visited[i] = false;
vertices[i].first = NULL;
}
}
void create() {
int row, col;
cin >> vexnum >> arcnum; //输入实际图的顶点数和边数
for (int i = 0; i < vexnum; i++) //输入顶点信息
cin >> vertices[i].data;
for (int i = 0; i < arcnum; i++) { //输入边信息
cin >> row >> col;
ArcNode *p = new ArcNode;
p->adjvex = col;
p->next = vertices[row].first;
vertices[row].first = p;
}
}
void DFSTrave() { //遍历图G
for (int u = 0; u < vexnum; u++) //对每个顶点u
if (visited[u] == false) //如果u未被访问
DFS(u); //访问u和u所在的连通块
}
};
int main() {
ALGraph<string> G;
G.create();
G.DFSTrave();
return 0;
}
邻接矩阵版:
#include <iostream>
using namespace std;
#define MaxVertexNum 100 //顶点数目的最大值
// VertexType,顶点的数据类型
template<typename VertexType>
class MGraph {
private:
VertexType Vex[MaxVertexNum]; //顶点表
int Edge[MaxVertexNum][MaxVertexNum]; //邻接矩阵,边表
int vexnum, arcnum; //图的当前顶点数和弧数
bool visited[MaxVertexNum]; //如果顶点i已被访问,则visited[i]==true。初值为false
void DFS(int u) { //u为当前访问的顶点索引
cout << Vex[u] << "\t";
visited[u] = true; //设置u已被访问
for (int i = 0; i < vexnum; i++)
if (Edge[u][i] == 1 && visited[i] == false)
DFS(i);
}
public:
MGraph() {
for (int i = 0; i < MaxVertexNum; i++) {
visited[i] = false;
for (int k = 0; k < MaxVertexNum; k++)
Edge[i][k] = 0;
}
}
void create() {
int row, col;
cin >> vexnum >> arcnum; //输入实际图的顶点数和边数
for (int i = 0; i < vexnum; i++) //输入顶点信息
cin >> Vex[i];
for (int i = 0; i < arcnum; i++) { //输入边信息
cin >> row >> col;
Edge[row][col] = 1;
}
}
void DFSTrave() { //遍历图G
for (int u = 0; u < vexnum; u++) //对每个顶点u
if (visited[u] == false) //如果u未被访问
DFS(u); //访问u和u所在的连通块
}
};
int main() {
MGraph<string> G;
G.create();
G.DFSTrave();
return 0;
}
二、拓扑排序
拓扑排序就是对一个有向无环图构造拓扑序列的过程:
1、在有向图中选一个没有前驱的顶点并输出。
2、从图中删除该顶点和所有以它为尾的弧。
代码如下:
#include <bits/stdc++.h>
using namespace std;
#define MAXVERTIES 20
#define OK 1
#define ERROR 0
int indegree[MAXVERTIES] = { 0 }; //用于存储入度信息
/*
5
1 2 3 4 5
6
1 2
1 4
1 3
2 4
3 5
4 5
*/
//定义结点
struct VexNode {
int data;
VexNode *next;
};
//定义弧
struct ArcNode {
int data;
VexNode *firstacr = NULL;
};
//定义邻接表
struct GraphList {
ArcNode arclist[MAXVERTIES];
int vexnum, arcnum;
};
//定义栈
struct Stack {
int Sacklist[MAXVERTIES] = { 0 };
int top = -1;
};
//入栈操作
void Push(Stack &S, int key) {
if (S.top == MAXVERTIES) {
cout << "栈已满!" << endl;
return;
}
S.top++;
S.Sacklist[S.top] = key;
}
//出栈操作
int Pop(Stack &S) {
if (S.top == -1) {
cout << "栈为空!" << endl;
return -1;
}
int temp = S.Sacklist[S.top];
S.top--;
return temp;
}
//返回结点在结点数组中的下标
int Location(GraphList &G, int key) {
for (int i = 0; i < G.vexnum; i++) {
if (G.arclist[i].data == key) {
return i;
}
}
return -1;
}
//创建图
void CreatGraph(GraphList &G) {
cout << "请输入顶点数:" << endl;
cin >> G.vexnum;
cout << "请输入顶点信息:" << endl;
for (int i = 0; i < G.vexnum; i++) {
cin >> G.arclist[i].data;
}
cout << "请输入弧数:" << endl;
cin >> G.arcnum;
cout << "请输入弧端点信息:" << endl;
for (int i = 0; i < G.arcnum; i++) {
int v1, v2;
cin >> v1 >> v2;
int Location1 = Location(G, v1);
int Location2 = Location(G, v2);
VexNode *new_node = new VexNode;
new_node->data = Location2;
new_node->next = G.arclist[Location1].firstacr;
G.arclist[Location1].firstacr = new_node;
indegree[Location2]++;
}
}
//拓扑排序
int TopoSort(GraphList &G, int *topolist) {
Stack S;
int topo = 0;
//先将所有入度为0的结点入栈
for (int i = 0; i < G.vexnum; i++) {
if (indegree[i] == 0) {
Push(S, i);
}
}
//依次出栈入度为0的结点
while (S.top != -1) {
int vx = Pop(S);
topolist[topo++] = G.arclist[vx].data; //输出结点
VexNode *temp = G.arclist[vx].firstacr;
//删除以该结点为尾的弧
while (temp != NULL) {
int index = temp->data;
indegree[index]--; //将该弧的弧头结点入度减1
//如果入度为0,则入栈
if (indegree[index] == 0) {
Push(S, index);
}
temp = temp->next;
}
}
topolist[topo] = -1;
//如果拓扑序列中的元素个数等于所有元素个数,则该图无环,否则该图有环
if (topo == G.vexnum) {
return OK;
} else {
return ERROR;
}
}
int main() {
GraphList GL;
CreatGraph(GL);
int topolist[MAXVERTIES] = { 0 };
int vx = TopoSort(GL, topolist);
if (!vx) {
cout << "有环!" << endl;
} else {
cout << "有向无环!" << endl;
cout << "拓扑序列如下:" << endl;
for (int i = 0; topolist[i] != -1; i++) {
cout << topolist[i] << " ";
}
}
return 0;
}