算法与数据结构(六)

一、图

一、临接表

表示方法如下:
在这里插入图片描述
带权值的无向图的构建:

#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;
}

猜你喜欢

转载自blog.csdn.net/qq_52302919/article/details/131494237