严蔚敏数据结构——图的基本

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_42877433/article/details/84142250
在学习图的相关概念后,对书中图的基本操作有许多的不解,经过学习众多大佬们的博客之后,对图的操作有了一些自己的想法。
#include<iostream>
using namespace std;
#define mvnum 100
1.首先,图的创建。1)图的创建有邻接表,邻接矩阵,十字链表等多种形式。在这里,先对图的邻接表形式进行讨论。
由书中的邻接表结构图,可以知道,邻接表需要一个边界点链表,一个头节点链表。在边界点结构中,需要由结点指向下一条边的指针,该边所指的顶点位置以及结点特殊信息组成。2)顶点结点由指向第一条依附该节点的边界点指针及顶点的数据域构成。3)图结构由由顶点个数,边的个数,边界点类型的结构构成。
typedef struct arcnode
{
 int adjvex;//该边所指的顶点位置 
 struct arcnode *nextarc;//指向下一条边的指针 
}arcnode;
typedef struct vnode
{
 char data;//顶点结点的信息;
 arcnode *firstnode;//指向第一条依附该节点的边界点指针 
}vonde,adjlist[mvnum];
typedef struct graph
{
 int vexnum,arcnum;
 adjlist vertices;//图中的表结构 
}graph;
2.创建图
	/*第一次写此段代码时,完全按照书中的照搬过来,却忽视了书中都是伪代码的事~~尴尬~~ 😅*/
	1)输入创建图的顶点个数及边的个数。vexnum为顶点个数,arcnum为边的个数。对顶点数组进行初始化,设置循环输入每个顶点的信息,并且将顶点的指针域置为NULL。
	2)设置循环,当k<arcnum时,输入图的边的两个结点,在图中找到对应结点的位置,更新p的adjvex值。
	3)将结点导入到对应顶点之后。(在此处有个操作//p->nextarc=G.vertices[i].firstnode;   G.vertices[i].firstnode==p;不是太明白)
void creatgraph(graph &g)
{
 char v1,v2;arcnode *p;
 printf("请输入表的点数\n");
 cin>>g.vexnum>>g.arcnum;
 int i,k,j;
 printf("请输入顶点信息\n");
 for(i=0;i<g.vexnum;i++)
 {
  cin>>g.vertices[i].data;
  g.vertices[i].firstnode=NULL;
 }
 for(k=0;k<g.arcnum;k++)
 {
  printf("请输入顶点之间的关系\n");
  cin>>v1>>v2;
  i=locatevex(g,v1);j=locatevex(g,v2);//找到v1与v2在顶点数组中的位置,由于是有向图,因此找到v1,v2后只将v2连在v1后即可
  p=new arcnode;
  p->adjvex=j;
  p->nextarc=g.vertices[i].firstnode;
  g.vertices[i].firstnode=p;
 }
}

3.输出图
在最开始,我以为是使用在课堂上学的dfs或者bfs,可是学校的练习系统上的示例如图。显然,这两种练习系统遍历方法只能输出顶点的遍历信息,不能输出每个结点所连接的结点。在学习过大佬博客后,发现所谓的输出,就是简单的输出…
1)当I<vexnum时,对图进行遍历,当G.vertics[i].firstnode!=null时,对每个结点进行遍历,同时也输出值

void display(graph p)
{
 int i;
 for(i=0;i<p.vexnum;i++)
 {
    printf("%d:%c ",i,p.vertices[i].data);
    while(p.vertices[i].firstnode!=NULL)
    {
   printf("%d ",p.vertices[i].firstnode->adjvex);
   p.vertices[i].firstnode=p.vertices[i].firstnode->nextarc;
    }
    printf("\n");
 }
}

3 DFS深度优先搜索遍历连通图
在解决这个问题时,我掉入了误区。首先,教材中的DFS输出的为数字而不是我想要的字符。其次,充斥了太多的伪代码,导致了理解的困难。在浏览了几位博主的博客后,我大致了解到了DFS的基本算法。
在实现这个功能的过程中,我一开始是想借用遍历图的思想,想通过几个循环解决,避开递归空间占据太大的弊端。但是最后无法实现回溯的过程,无奈放弃。之后,通过增加一个标志数组来记录结点是否被访问过可以轻松的解决回溯的问题。
1)找到起始数字在顶点数组中代表的字符,输出起始顶点,并且将该字符的标志数组置为1.
2)创建一个新的指针q(用来遍历边界点表),让q指向该顶点的firstices,当q不为空的时候循环。
3)如果在标志数组中q的adjvex在数组中的值为0,则对他进行DFS递归,否则,将q往后移动。

bool visited[mvnum];
void dfs(graph p,int v)
{
 arcnode *q;
 cout<<p.vertices[v].data<<' ';
 visited[v]=1;
 q=p.vertices[v].firstnode;
 while(q)
 {
  if(visited[q->adjvex]==0)
  {
   dfs(p,q->adjvex);
  }
  q=q->nextarc;
 }
}

在一开始,我将visited放在图结构中,这就造成了回溯时发生问题(c语言语法对不报错,不知道其他语言是否也是这样,有大神明白了私信我呗。)因此应将visited置为全局变量。实验测试
4.十字链表的创建图的方法
邻接表可以实现图的许多操作,但是在计算图结点的出度入度问题时,不能很好的解决问题,因此,应该掌握更多的图的创建方法。在邻接表的基础上,增加入度结点的指针,可以解决该问题。(在一开始,我在思考如何实现边结点的链接,而没有在邻接表的基础上进行思考,陷入了无法操作的误区。之后,我在邻接表的基础上重新思考,找到了解决方法。)
1)定义基础的头节点与边结点的存储结构。(头节点的存储由头节点的信息,该节点出边的指针域,入边的指针域构成)(边结点由数据信息,tailvex该节点对应边的起始结点(但此节点为该头节点的尾节点,后面那个同理),headvex该结点对应边的终止结点,入边表指针域,出边表指针域)
2)创建链表在开始时比较迷茫,主要是没有搞清楚入度的操作情况,在明白入度表操作后,十字链表的创建就容易多了。
3)在输出该链表时,我根据题目需要,运用了三个循环将该表输出。
代码如下:

void creatgraph(graph &p)
{
 printf("请输入图的节点数和边数\n");
 cin>>p.vexnum>>p.arcnum;
 int i,k,j;char v1,v2;arcnode *m,*n;
 printf("请输入头节点信息\n");
 for(i=0;i<p.vexnum;i++)
 {
  cin>>p.vertices[i].data;
  p.vertices[i].firstin=NULL;
  p.vertices[i].firstout=NULL;
 }
 for(k=0;k<p.arcnum;k++)
 {
  cin>>v1>>v2;
  i=locatevex(p,v1);j=locatevex(p,v2);
  m=new arcnode;
  m->tailvex=i;
  m->taillink=p.vertices[i].firstout;
  p.vertices[i].firstout=m;
  n=new arcnode;
  n->headvex=i;
  n->headlink=p.vertices[j].firstin;
  p.vertices[j].firstin=n;
 }
}```

void display(graph p)
{
int i;arcnode *q;
for(i=0;i<p.vexnum;i++)
{
int k=0,j=0,sum=0;
q=p.vertices[i].firstout;
while(q)
{
k++;
q=q->taillink;
}//找到出度数
q=p.vertices[i].firstin;
while(q)
{
j++;
q=q->headlink;
}
sum=k+j;
cout<<p.vertices[i].data<<" “<<j<<” “<<k<<” “<<sum<<” “<<”\n";
}
}

图片传不上了,测试数据如下:

6 6
A B C D E F
A B
A C
B D
C D
D E
C F
展开阅读全文

没有更多推荐了,返回首页