图的存储结构,可以有邻接表表示和矩阵表示两种。我们实现邻接表表示(矩阵表示用一个二维数组就可以):
- #include
- #include
- #include
- #define maxvex 5
- #define ElemType char
-
- //边表结点
- typedef struct edgenode{
- int adjvex; //下标
- int weight; //权值,可有可没有
- struct edgenode *next;
- }EdgeNode;
-
- //顶点结点
- typedef struct node{
- int data;
- EdgeNode *first; //边表头指针
- }VertexNode,AdjList[maxvex];
-
- //二次封装
- typedef struct {
- AdjList adjlist;
- int numetex, numedge;
- }GraAdjList;
-
- //创建邻接表
- void CreateAlGraph(GraAdjList &g,int x)
- { //i=0创建有向图,i=1创建无向图
- int m, n;
- EdgeNode *e = NULL;
- EdgeNode *q = NULL;
- printf("请输入顶点数和边数(无向图端点相同只需录入一条边):");
- scanf("%d %d", &g.numetex, &g.numedge);
- printf("请输入顶点的信息:");
-
- //获取顶点的信息
- for (int i = 0;i < g.numetex;i++)
- {
- scanf("%d", &g.adjlist[i].data);
- g.adjlist[i].first = NULL;
- }
- //建立边表,链表采用头插法
- for (int k = 0; k < g.numedge; k++)
- {
- printf("请输入边(Vi,Vj)的顶点下标:");
- scanf("%d %d", &m, &n);
- //链表结点
- if(x == 0){
- e = (EdgeNode *)malloc(sizeof(EdgeNode));
- e->adjvex = n;
- e->next = NULL;
- //头插法建链
- if (g.adjlist[m].first == NULL)
- q = g.adjlist[m].first = e;
- else
- q = q->next = e;
- }
- else{
- e = (EdgeNode *)malloc(sizeof(EdgeNode));
- e->adjvex = n;
- e->next = NULL;
- if (g.adjlist[m].first == NULL)
- q = g.adjlist[m].first = e;
- else
- q = q->next = e;
- //对无向图,一个边要在两个结点都增加
- e = (EdgeNode *)malloc(sizeof(EdgeNode));
- e->adjvex = m;
- e->next = NULL;
- if (g.adjlist[n].first == NULL)
- q = g.adjlist[n].first = e;
- else
- q = q->next = e;
- }
- }
- }
- void print(const GraAdjList &g){
- EdgeNode *p;
- for (int k = 0;k < g.numetex;k++){
- printf("顶点%d: ", k);
- for (p = g.adjlist[k].first; p;p = p->next)
- if (p != NULL)
- printf("%d ", p->adjvex);
- printf("\n");
- }
- }
-
- int main(){
- GraAdjList gralist;
- CreateAlGraph(gralist, 1); //i=0创建有向图,i=1创建无向图
- print(gralist);
- Graphtomartix(gralist);
- }
输出:
- 请输入顶点数和边数(无向图端点相同只需录入一条边):3 1
- 请输入顶点的信息:0 1 2
- 请输入边(Vi,Vj)的顶点下标:0 1
- 顶点0: 1
- 顶点1: 0
- 顶点2:
然后我们再实现图的广度优先遍历(BFS):
- #include
- #include
- #include
- #define maxvex 10
- #define MAXSIZE 10
-
- //边表结点
- typedef struct edgenode{
- int adjvex; //下标
- int weight; //权值,可有可没有
- struct edgenode *next;
- }EdgeNode;
-
- //顶点结点
- typedef struct node{
- int data;
- EdgeNode *first; //边表头指针
- }VertexNode,AdjList[maxvex];
-
- //二次封装
- typedef struct {
- AdjList adjlist;
- int numetex, numedge;
- }GraAdjList;
-
- //创建邻接表
- void CreateAlGraph(GraAdjList &g,int x)
- { //i=0创建有向图,i=1创建无向图
- int m, n;
- EdgeNode *e = NULL;
- EdgeNode *q = NULL;
- printf("请输入顶点数和边数(无向图端点相同只需录入一条边):");
- scanf("%d %d", &g.numetex, &g.numedge);
- //获取顶点的信息
- for (int i = 0;i < g.numetex;i++){
- g.adjlist[i].data = i;
- g.adjlist[i].first = NULL;
- }
- //建立边表,链表采用头插法
- for (int k = 0; k < g.numedge; k++){
- printf("请输入边(Vi,Vj)的顶点下标:");
- scanf("%d %d", &m, &n);
- //链表结点
- if(x == 0){
- e = (EdgeNode *)malloc(sizeof(EdgeNode));
- e->adjvex = n;
- e->next = NULL;
- //头插法建链
- if (g.adjlist[m].first == NULL)
- q = g.adjlist[m].first = e;
- else
- q = q->next = e;
- }
- else{
- e = (EdgeNode *)malloc(sizeof(EdgeNode));
- e->adjvex = n;
- e->next = NULL;
- q = g.adjlist[m].first;
- g.adjlist[m].first = e;
- g.adjlist[m].first->next = q;
- //对无向图,一个边要在两个结点都增加
- e = (EdgeNode *)malloc(sizeof(EdgeNode));
- e->adjvex = m;
- e->next = NULL;
- q = g.adjlist[n].first;
- g.adjlist[n].first = e;
- g.adjlist[n].first->next = q;
- }
- }
- }
-
- typedef struct Queue{
- int data[MAXSIZE];
- int front, rear; //头尾指针,队头指针指向第一个元素,队尾指针指向队尾元素的下一个元素
- int tag;
- }Queue;
-
- //初始化
- int InitQueue(Queue &a){
- a.front = 0;
- a.rear = 0;
- a.tag = 0; //初始队列是空
- return 1;
- }
- //判断栈和队列是否为空
- bool IsQueueEmpty(Queue q){
- if(q.tag == 0 && q.front==q.rear)
- return true;
- else
- return false;
- }
- //插入
- int InsertQueue(Queue &a, int x){
- if(a.front == a.rear && a.tag == 1){
- printf("当前队列已满!");
- return 0;
- }
- else{
- a.data[a.rear] = x;
- a.tag = 1; //插入队列tag置1
- a.rear = (a.rear + 1) % MAXSIZE;
- return 1;
- }
- }
- //删除
- int DeleteQueue(Queue &a){
- int x;
- if(a.front == a.rear && a.tag == 0){
- printf("当前队列已空!");
- }
- else{
- x = a.data[a.front];
- a.tag = 0; //删除队列tag置0
- a.front = (a.front + 1) % MAXSIZE; //front指针+1
- return x;
- }
- }
-
- void print(const GraAdjList &g){
- EdgeNode *p;
- for (int k = 0;k < g.numetex;k++){
- printf("顶点%d: ", k);
- for (p = g.adjlist[k].first; p;p = p->next){
- if (p != NULL)
- printf("%d ", p->adjvex);
- }
- printf("\n");
- }
- }
-
- //根据邻接表转换成矩阵
- void Graphtomartix(GraAdjList &g){
- EdgeNode *p;
- int a[g.numetex][g.numetex];
- //置零
- for (int i = 0; i < g.numetex;i++){
- for (int j = 0; j < g.numetex;j++)
- a[i][j] = 0;
- }
- //赋值
- for (int i = 0; i < g.numetex; i++){
- for (p = g.adjlist[i].first; p; p = p->next){
- a[i][p->adjvex] = 1;
- }
- }
- //打印
- for (int i = 0; i < g.numetex;i++){
- for (int j = 0; j < g.numetex;j++)
- printf("%d ", a[i][j]);
- printf("\n");
- }
- }
-
- void BFSTraverse(GraAdjList g,int v){ //v代表广度优先从哪开始
- bool visited[g.numetex];
- Queue q;
- EdgeNode *p;
- for (int i = 0; i < g.numetex;i++)
- visited[i] = false;
- InitQueue(q);
- printf("%d", v); //输出访问结点
- visited[v] = true;
- InsertQueue(q, v); //进队
- while (!IsQueueEmpty(q)){
- int x = DeleteQueue(q);
- for (p = g.adjlist[x].first; p != NULL;p=p->next){
- if(visited[p->adjvex]==false){
- printf("%d", p->adjvex);
- visited[p->adjvex] = true;
- InsertQueue(q, p->adjvex);
- }
- }
- }
-
- for (int i = 0; i < g.numetex;i++){ //检查连通分量
- if(visited[i] == false){
- printf("%d", i); //输出访问结点
- visited[i] = true;
- InsertQueue(q, i); //进队
- while (!IsQueueEmpty(q)){
- int x = DeleteQueue(q);
- for (p = g.adjlist[x].first; p != NULL;p=p->next){
- if(visited[p->adjvex]==false){
- printf("%d", p->adjvex);
- visited[p->adjvex] = true;
- InsertQueue(q, p->adjvex);
- }
- }
- }
- }
- }
- }
-
- int main(){
- GraAdjList gralist;
- CreateAlGraph(gralist, 1); //i=0创建有向图,i=1创建无向图
- print(gralist);
- printf("图所对应的邻接矩阵是:");
- Graphtomartix(gralist);
- printf("图的广度优先遍历序列是:");
- BFSTraverse(gralist, 0);
- }
输出:
- 请输入顶点数和边数(无向图端点相同只需录入一条边):8 7
- 请输入边(Vi,Vj)的顶点下标:0 1
- 请输入边(Vi,Vj)的顶点下标:0 2
- 请输入边(Vi,Vj)的顶点下标:1 3
- 请输入边(Vi,Vj)的顶点下标:1 4
- 请输入边(Vi,Vj)的顶点下标:2 5
- 请输入边(Vi,Vj)的顶点下标:2 6
- 请输入边(Vi,Vj)的顶点下标:4 7
- 顶点0: 2 1
- 顶点1: 4 3 0
- 顶点2: 6 5 0
- 顶点3: 1
- 顶点4: 7 1
- 顶点5: 2
- 顶点6: 2
- 顶点7: 4
- 0 1 1 0 0 0 0 0
- 1 0 0 1 1 0 0 0
- 1 0 0 0 0 1 1 0
- 0 1 0 0 0 0 0 0
- 0 1 0 0 0 0 0 1
- 0 0 1 0 0 0 0 0
- 0 0 1 0 0 0 0 0
- 0 0 0 0 1 0 0 0
- 图的广度优先遍历序列是:02165437
-
- 请输入顶点数和边数(无向图端点相同只需录入一条边):5 4
- 请输入边(Vi,Vj)的顶点下标:0 1
- 请输入边(Vi,Vj)的顶点下标:0 2
- 请输入边(Vi,Vj)的顶点下标:1 2
- 请输入边(Vi,Vj)的顶点下标:3 4
- 顶点0: 2 1
- 顶点1: 2 0
- 顶点2: 1 0
- 顶点3: 4
- 顶点4: 3
- 0 1 1 0 0
- 1 0 1 0 0
- 1 1 0 0 0
- 0 0 0 0 1
- 0 0 0 1 0
- 图的广度优先遍历序列是:02134
-
- //此示例要把main最后一行改成BFSTraverse(gralist, 2),表示从2开始广度优先遍历
- 请输入顶点数和边数(无向图端点相同只需录入一条边):8 7
- 请输入边(Vi,Vj)的顶点下标:0 1
- 请输入边(Vi,Vj)的顶点下标:0 2
- 请输入边(Vi,Vj)的顶点下标:1 3
- 请输入边(Vi,Vj)的顶点下标:1 4
- 请输入边(Vi,Vj)的顶点下标:2 5
- 请输入边(Vi,Vj)的顶点下标:2 6
- 请输入边(Vi,Vj)的顶点下标:4 7
- 顶点0: 2 1
- 顶点1: 4 3 0
- 顶点2: 6 5 0
- 顶点3: 1
- 顶点4: 7 1
- 顶点5: 2
- 顶点6: 2
- 顶点7: 4
- 0 1 1 0 0 0 0 0
- 1 0 0 1 1 0 0 0
- 1 0 0 0 0 1 1 0
- 0 1 0 0 0 0 0 0
- 0 1 0 0 0 0 0 1
- 0 0 1 0 0 0 0 0
- 0 0 1 0 0 0 0 0
- 0 0 0 0 1 0 0 0
- 图的广度优先遍历序列是:26501437
然后我们看深度优先遍历算法:
- bool visited[8]; //bool visited[g.numetex],此处修改为图的顶点数
- void DFS(GraAdjList g,int v){
- EdgeNode *p;
- printf("%d", v);
- visited[v] = true;
- for (p = g.adjlist[v].first; p != NULL;p = p->next){
- if(!visited[p->adjvex])
- DFS(g, p->adjvex);
- }
- }
- void DFSTraverse(GraAdjList g){ //v代表深度优先从哪开始
- for (int i = 0; i < 8;i++) //赋初值,这里也要修改为图的顶点数
- visited[i] = false;
- for (int i = 0; i < 8;i++){ //赋初值,这里也要修改为图的顶点数
- if(!visited[i])
- DFS(g, i);
- }
- }
-
- int main(){
- GraAdjList gralist;
- CreateAlGraph(gralist, 1); //i=0创建有向图,i=1创建无向图
- print(gralist);
- printf("图所对应的邻接矩阵是:\n");
- Graphtomartix(gralist);
- printf("图的深度优先遍历序列是:");
- DFSTraverse(gralist);
- }
输出:
- 请输入顶点数和边数(无向图端点相同只需录入一条边):8 7
- 请输入边(Vi,Vj)的顶点下标:0 1
- 请输入边(Vi,Vj)的顶点下标:0 2
- 请输入边(Vi,Vj)的顶点下标:1 3
- 请输入边(Vi,Vj)的顶点下标:1 4
- 请输入边(Vi,Vj)的顶点下标:2 5
- 请输入边(Vi,Vj)的顶点下标:2 6
- 请输入边(Vi,Vj)的顶点下标:4 7
- 顶点0: 2 1
- 顶点1: 4 3 0
- 顶点2: 6 5 0
- 顶点3: 1
- 顶点4: 7 1
- 顶点5: 2
- 顶点6: 2
- 顶点7: 4
- 图所对应的邻接矩阵是:
- 0 1 1 0 0 0 0 0
- 1 0 0 1 1 0 0 0
- 1 0 0 0 0 1 1 0
- 0 1 0 0 0 0 0 0
- 0 1 0 0 0 0 0 1
- 0 0 1 0 0 0 0 0
- 0 0 1 0 0 0 0 0
- 0 0 0 0 1 0 0 0
- 图的深度优先遍历序列是:02651473
试题1(王道6.2.6节综合题4):
写出从图的邻接表表示转换成邻接矩阵表示的算法。
和前面的练习题相比,此题几乎就是白给。。。
- //根据邻接表转换成矩阵
- void Graphtomartix(GraAdjList &g){
- EdgeNode *p;
- int a[g.numetex][g.numetex];
- //置零
- for (int i = 0; i < g.numetex;i++){
- for (int j = 0; j < g.numetex;j++)
- a[i][j] = 0;
- }
- //赋值
- for (int i = 0; i < g.numetex; i++){
- for (p = g.adjlist[i].first; p; p = p->next){
- a[i][p->adjvex] = 1;
- }
- }
- //打印
- for (int i = 0; i < g.numetex;i++){
- for (int j = 0; j < g.numetex;j++)
- printf("%d ", a[i][j]);
- printf("\n");
- }
- }
输出:
- 请输入顶点数和边数(无向图端点相同只需录入一条边):3 1
- 请输入顶点的信息:0 1 2
- 请输入边(Vi,Vj)的顶点下标:0 1
- 顶点0: 1
- 顶点1: 0
- 顶点2:
- 0 1 0
- 1 0 0
- 0 0 0
试题2(王道6.3.4节综合题2):
设计算法判断一个无向图G是否是一棵树。
图成为树的标准:n个顶点,n-1条边,而我们建立的图的数据结构种numetex, numedge刚好对应这两个参数,所以此题等于白给。。。
- //此算法判断图是不是一棵树
- bool ifTree(GraAdjList g){
- if(g.numetex-g.numedge==1)
- return true;
- else
- return false;
- }
试题3(王道6.3.4节综合题3):
写出图的深度优先搜索DFS算法的非递归算法(图采用邻接表形式)。
非递归算法,把递归转化成非递归那就上栈被:
- bool visited[8]; //bool visited[g.numetex],此处修改为图的顶点数
-
- void DFS(GraAdjList g,Sqstack S,int v){
- int x;
- EdgeNode *p;
- InsertSqstack(S, v);
- while(!IsStackEmpty(S)){
- x = DeleteSqstack(S);
- printf("%d", x);
- visited[x] = true;
- for(p = g.adjlist[x].first; p != NULL;p = p->next){
- if(visited[p->adjvex]==false){
- InsertSqstack(S, p->adjvex);
- visited[p->adjvex] = true;
- }
- }
- }
- }
- void DFSTraverse(GraAdjList g,Sqstack S){ //v代表深度优先从哪开始
- for (int i = 0; i < 8;i++) //赋初值,这里也要修改为图的顶点数
- visited[i] = false;
- for (int i = 0; i < 8;i++){ //赋初值,这里也要修改为图的顶点数
- if(!visited[i])
- DFS(g, S, i);
- }
- }
-
- int main(){
- GraAdjList gralist;
- CreateAlGraph(gralist, 1); //i=0创建有向图,i=1创建无向图
- print(gralist);
- printf("图所对应的邻接矩阵是:\n");
- Graphtomartix(gralist);
- Sqstack S;
- InitStack(S);
- printf("图的深度优先遍历序列是:");
- DFSTraverse(gralist, S);
- }
输出:
- 请输入顶点数和边数(无向图端点相同只需录入一条边):8 7
- 请输入边(Vi,Vj)的顶点下标:0 1
- 请输入边(Vi,Vj)的顶点下标:0 2
- 请输入边(Vi,Vj)的顶点下标:1 3
- 请输入边(Vi,Vj)的顶点下标:1 4
- 请输入边(Vi,Vj)的顶点下标:2 5
- 请输入边(Vi,Vj)的顶点下标:2 6
- 请输入边(Vi,Vj)的顶点下标:4 7
- 顶点0: 2 1
- 顶点1: 4 3 0
- 顶点2: 6 5 0
- 顶点3: 1
- 顶点4: 7 1
- 顶点5: 2
- 顶点6: 2
- 顶点7: 4
- 图所对应的邻接矩阵是:
- 0 1 1 0 0 0 0 0
- 1 0 0 1 1 0 0 0
- 1 0 0 0 0 1 1 0
- 0 1 0 0 0 0 0 0
- 0 1 0 0 0 0 0 1
- 0 0 1 0 0 0 0 0
- 0 0 1 0 0 0 0 0
- 0 0 0 0 1 0 0 0
- 图的深度优先遍历序列是:01347256
试题4(王道6.3.4节综合题4):
分别采用广度优先遍历和深度优先遍历算法判断以邻接表方式存储的有向图中是否存在由顶点i到顶点j的路径(i与j不相等)。
此题实际上就是检查两个结点是否在一个连通分量里,把前面的for循环删掉然后加判断visited[j]语句即可。
试题5(王道6.3.4节综合题5):
假设图用邻接表表示,设计算法输出从顶点i到顶点j的所有简单路径。
此题的算法不是很好想。除了要记录每个顶点是否被访问(visited数组),还要建立path数组记录当前搜索路径。函数实现的思路是:
需要注意的是,函数调用前需要将visited数组初始化为false,path数组初始化为空。另外,函数调用时初始的d值应为-1。
- bool visited[4]; //bool visited[g.numetex],此处修改为图的顶点数
- int path[8];
-
- void FindPath(GraAdjList g,int u,int v,int path[],int d){
- int w;
- int i;
- EdgeNode *p;
- d = d + 1; //搜索路径长度+1,初始为-1
- path[d] = u; //搜索路径第d步上是u
- visited[u] = true;
- if(u == v){
- printf("找到一条路径如下:");
- for (int i = 0; i <= d;i++){
- printf("%d ", path[i]);
- }
- printf("\n");
- }
- p = g.adjlist[u].first; //p指向u的第一个邻接点
- while(p!=NULL){
- w = p->adjvex;
- if(visited[w]==false){
- FindPath(g, w, v, path, d);
- }
- p = p->next;
- }
- visited[u] = false; //重置
- }
-
- int main(){
- GraAdjList gralist;
- CreateAlGraph(gralist, 1); //i=0创建有向图,i=1创建无向图
- print(gralist);
- printf("图所对应的邻接矩阵是:\n");
- Graphtomartix(gralist);
- FindPath(gralist, 0, 3, path, -1);
- }
输出:
- 请输入顶点数和边数(无向图端点相同只需录入一条边):4 5
- 请输入边(Vi,Vj)的顶点下标:0 1
- 请输入边(Vi,Vj)的顶点下标:0 2
- 请输入边(Vi,Vj)的顶点下标:1 2
- 请输入边(Vi,Vj)的顶点下标:1 3
- 请输入边(Vi,Vj)的顶点下标:2 3
- 顶点0: 2 1
- 顶点1: 3 2 0
- 顶点2: 3 1 0
- 顶点3: 2 1
- 图所对应的邻接矩阵是:
- 0 1 1 0
- 1 0 1 1
- 1 1 0 1
- 0 1 1 0
- 找到一条路径如下:0 2 3
- 找到一条路径如下:0 2 1 3
- 找到一条路径如下:0 1 3
- 找到一条路径如下:0 1 2 3
试题6(王道6.4.6节第6题):
试说明利用DFS算法如何实现有向无环图的拓扑排序。
利用递归特性,若拓扑排序序列中,a在b之后,则递归的时候a比b靠前递归结束。利用这一特点,增设time变量,然后对time变量从大到小输出即可:
- bool visited[8]; //bool visited[g.numetex],此处修改为图的顶点数
- int finishtime[8];
- int time = 0;
- void DFS(GraAdjList g,int v){
- EdgeNode *p;
- visited[v] = true;
- for (p = g.adjlist[v].first; p != NULL;p = p->next){
- if(!visited[p->adjvex])
- DFS(g, p->adjvex);
- }
- time = time + 1;
- finishtime[v] = time;
- printf("(%d,%d)", finishtime[v], v);
- }
- void DFSTraverse(GraAdjList g){ //v代表深度优先从哪开始
- for (int i = 0; i < 8;i++) //赋初值,这里也要修改为图的顶点数
- visited[i] = false;
- for (int i = 0; i < 8;i++){
- if(!visited[i])
- DFS(g, i);
- }
- }
-
- int main(){
- GraAdjList gralist;
- CreateAlGraph(gralist, 0); //i=0创建有向图,i=1创建无向图
- print(gralist);
- printf("%d", time);
- printf("图所对应的邻接矩阵是:\n");
- Graphtomartix(gralist);
- printf("图的拓扑排序序列是:");
- DFSTraverse(gralist);
- }
输出:
- 输入顶点数和边数(无向图端点相同只需录入一条边):8 7
- 请输入边(Vi,Vj)的顶点下标:0 1
- 请输入边(Vi,Vj)的顶点下标:1 2
- 请输入边(Vi,Vj)的顶点下标:2 3
- 请输入边(Vi,Vj)的顶点下标:3 7
- 请输入边(Vi,Vj)的顶点下标:4 5
- 请输入边(Vi,Vj)的顶点下标:5 6
- 请输入边(Vi,Vj)的顶点下标:6 7
- 顶点0: 1
- 顶点1: 2
- 顶点2: 3
- 顶点3: 7
- 顶点4: 5
- 顶点5: 6
- 顶点6: 7
- 顶点7:
- 0图所对应的邻接矩阵是:
- 0 1 0 0 0 0 0 0
- 0 0 1 0 0 0 0 0
- 0 0 0 1 0 0 0 0
- 0 0 0 0 0 0 0 1
- 0 0 0 0 0 1 0 0
- 0 0 0 0 0 0 1 0
- 0 0 0 0 0 0 0 1
- 0 0 0 0 0 0 0 0
- 图的拓扑排序序列是:(1,7)(2,3)(3,2)(4,1)(5,0)(6,6)(7,5)(8,4)
所以可得到拓扑排序序列:45601237