图的存储结构再总结
来源:互联网 发布:中韩农产品贸易数据 编辑:程序博客网 时间:2024/06/05 17:23
图的邻接矩阵存储在Prim算法这部分有提及。
http://blog.csdn.net/u011240016/article/details/52414818
//使用邻接矩阵存储#include<stdio.h>#include<stdlib.h>#define MAX_VERTEX_NUM 20#define INF 10000 // 表示顶点之间不可达#define FALSE 0#define TRUE 1typedef int bool;typedef struct ArcCell //若info用不到,可以直接定义AdjMatrix[20][20],这里用最难的方式实现{ int adj;//存储边之间的权值 // int info; //这里用不到} ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];// 定义图typedef struct{ int vexs[MAX_VERTEX_NUM];//顶点向量,存储顶点内容 AdjMatrix arcs;//存储顶点之间的关系 int vexnum, arcnum;//图的顶点数和边数}MGraph;int LocateElem(MGraph *G, int c){ for(int i = 0; i < G->vexnum;i++) { if(G->vexs[i] == c) return i; } return MAX_VERTEX_NUM; //返回一个矩阵中不用的下标数字即可}void CreateGraph(MGraph *G)//构造图{ printf("请输入顶点数,边数:\n"); scanf("%d%d",&G->vexnum,&G->arcnum);//输入图的顶点数目和边数目 // printf("请输入顶点内容,顶点数为%d\n",G->vexnum); for(int i = 0; i < G->vexnum; i++)//初始化图的顶点 { // printf("输入结点%d: ",i); scanf("%d",&G->vexs[i]); //输入顶点信息:a,b,c,d...,假设顶点值各不相同 } printf("结点内容输入完毕!---\n"); //初始化邻接矩阵 for(int i = 0; i < G->vexnum; i++) { for(int j = 0; j < G->vexnum; j++) { G->arcs[i][j].adj = INF;//设置顶点间的距离都是INF } } //构造邻接矩阵 printf("请输入边:\n"); for(int k = 0; k < G->arcnum; k++) { // printf("请输入边:\n"); int u,v;//u,v分别为顶点存储内容 int w; //w是边的权值 scanf("%d %d %d",&u,&v,&w); //输入边 // getchar(); //根据顶点内容找到结点的下标--在一维数组中寻找 int i = LocateElem(G,u); int j = LocateElem(G,v); G->arcs[i][j].adj = w; G->arcs[j][i].adj = w; //无向,所以对称边也赋值 }}
简单总结就是用矩阵存储边的信息,一般一个int型数据就可以了,但是我们用了ArcCell这样的结构体去存储,不仅包含边的权值(假设0,1也称之为权值好了),利于以后的扩展。
需要打消的误区是,用了结构体好像马上就要用malloc,马上需要调出指针来一番堆存储了,这些不是必须的,而且,即使需要用到堆,那也是应该感到很自然的事情。
说到这里,开始感觉到为什么CS叫Math in Computer.
为什么数据结构如此重要了。
你空有数学的解法,转移不到计算机中来,那么算法是学不好的。
所以你需要能够灵活使用各种数据结构。
可以想象,最初是没有数据结构这种称呼的,只是在解决问题的过程中,人类发现了其中的pattern,我们总是擅长发现各种事物时间的pattern不是吗?
我知道开始很多人和我一样,学到一个高级的数据结构,觉得很了不得,以前学的那些基本数据结构都弱爆了什么的,要是问题都能用这些高级的数据结构解决,算法从根子上就比人家的 “高级”。比如最初学习图的存储,我觉得用邻接表就比邻接矩阵高级,因为邻接矩阵看着太像二维数组“而已”,邻接表呢,必须用到指针,成链,非顺序存储等等,这种狭义的偏见。
如果你也和我一样曾经有过甚至现在还有,我希望你也能和我一样尝试着深呼吸,然后仔细审查题目,思考,这些数据结构是什么。
不妨从结构体开始。
在邻接矩阵的边的关系中,我们用的是权值来表示,按理说用个数字就够了,我们偏偏搞出个大动作,非要存储结构体。那么结构体和一个数字有什么大的区别吗?
并没有。
是不是我们也只在结构体中存了一个数字而已,所以,把结构体看做一个盒子就可以了。
你想光秃秃的用个数字,我非要给它加个包装盒,还起了一个听着就知道什么东西的名字ArcCell,好像专门设计存放在矩阵中的一个一个格子中的。
一旦你吸收了这种思想,你会深深爱上结构体。
所以,不仅仅是只有邻接表,链表这种需要结点的地方才用结构体,只要你想把数据包装一下,尽情用好了!
而在单链表,邻接表这种非线性存储结构中,使用结构体+指针则是必备武器了。
好了,开始总结邻接表的设计。
我假设你已经知道:
设计思路:针对每一个顶点建立一个单链表,第i个单链表中的结点表示依附于顶点vi的边(对于有向图,则是以顶点vi结尾的弧)。
单链表中结点称之为表结点:有三个域。
- 邻接点域(adjvex):指示与顶点vi邻接的点在图中的位置,所以是int型
- 链域(next arc):指示下一条边或弧的结点
- 数据域(info) :存储和边或者弧相关的信息,如权值等,这个可以不用。
头结点有两个域:
- 链域(firstarc):指向链表中的第一个结点
- 数据域(data): 存储顶点vi的名或者其他有关信息
// --------图的邻接存储表示#define MAX_VERTEX_NUM 20typedef struct ArcNode{ int adjvex; //该弧指向的顶点在顶点表中的位置 struct ArcNode *nextarc; //指向下一条弧的指针 InfoType *info; //该弧相关信息的指针};typedef struct VNode{ VertexType data; //顶点信息 ArcNode *firstarc; // 指向第一条依附于该顶点的弧的指针} VNode,AdjList[MAX_VERTEX_NUM];typedef struct { AdjList vertices; int vexnum,arcnum; //图的当前顶点数和弧数 int kind;//图的种类标志} ALGraph;
吃透细节以后,才发现这个定义的模板是很漂亮的。
我努力以一种人说的话讲出我的理解。
首先,为什么设计这种邻接表的结构?
你看在二叉树里面,我们只需要定义一种标准的结点形式,以后只需要针对结点里面的指针进行串联或者删除链接,更改链接等等操作即可。为什么在树可以,而在图不可以?到底是怎样的不同让他们变得如此迥异?
以我的理解,答案是:
我们常用的树的结点指向的结点数目是可预知的。
比如二叉树,一个结点最多指向下两个结点,左右孩子嘛。
图呢?不行。图的结点一个可以连向多少个其他结点?不知道。
因此,树中的存储策略,到这里就不行了。
但是核心仍然是结点的存储思路。
这么一想,你就明白,设计出邻接表这种结构的同学,也是挺酷的–居然可以描述图的结构。
那么看是怎么做到的。
首先,我们不看结点之间的关系。那么,一个结点承载的信息就和树没有什么大不同。所以,把这些朴素的家伙放在一个数组里。为啥这样做?因为顺序存储有很好的随机存取的属性嘛!同时,下面会说到,存储结点之间关系下标也是很重要的。
这样的结点我们称之为头结点。
typedef struct VNode{ VertexType data; //顶点信息 ArcNode *firstarc; // 指向第一条依附于该顶点的弧的指针} VNode,AdjList[MAX_VERTEX_NUM];
真是奇奇怪怪的名字。
对了,data属性为什么用VertexType,这是啥?哈哈,严奶奶是一个很酷的人,她示范我们,做事情要做的漂亮,比如data的类型,现在存的是int型,突然改了,想变成char型呢?所以在头部定义一个:
`typedef int VertexType;`
int变char就是一行代码的事情:
`typedef int VertexType;`
对了,这是不是叫泛型的设计思路?
所以,都是为了更好的偷懒,才发明了更好的解法。
因此:
懒是人类的希望之光。
Laziness is the light of the future.
呵呵哒。
好的,还是回到正题上,既然结点设么设计,还这么简洁,data域不用多讲。为什么每个结点还伸出去个钩子?钩向一个看起来叫ArcNode类型的未知的东西!
脑海中有没有这样一个画面,在浴室中的挂毛巾的挂钩那一排钩子,对应着这个结点里的指针。挂钩用来挂一些常用的毛巾,那么这个突出的指针想挂着什么?
那么得探讨表结点的设计了。
邻接表存储的边的信息全靠单链表抗着呢。
无向图
在无向图中,顶点表(就是那个存储数据类型为VNode的顺序表啊)的每一个元素,通过挂钩挂着一个与此元素相连的结点,这个结点呢再往后挂一个结点,挂的是什么样的结点呢,还是与顺序表中的元素相连的结点。所以一个元素挂成了一个单链表。这个单链表表示的是与顺序表中的这个元素相邻的边。
因此,有多少个头就有多少个单链表。这下知道头结点的来历了吧!头结点指向的第一个结点,怎么开心怎么选择。
有向图
有向图的话,单链表的结点是顺序表中指向的结点。其他的没有什么不同。
typedef struct ArcNode{ int adjvex; //该弧指向的顶点在顶点表中的位置 struct ArcNode *nextarc; //指向下一条弧的指针 InfoType *info; //该弧相关信息的指针};
adjvex指向在顺序表中的数组下标。
nextarc指向下一个结点。
自顶向下的思考,自底向上的实现。
最终,我们需要的数据结构是有Node组装的图:
typedef struct { AdjList vertices; int vexnum,arcnum; //图的当前顶点数和弧数 int kind;//图的种类标志} ALGraph;
同样,这里有结点数目,边数目,还有头结点的顺序表。
问:怎么不关心单链表?答案是,你知道有多少单链表的结点么?
所以要动态生成,也即每次malloc一个ArcNode,并通过顺序表的那个钩子,钩起来!
所以生成过程应是怎样的?
了解一只青蛙的最好方式是自己造一个青蛙。
HOPE.
- 图的存储结构再总结
- 图的存储结构
- 图的存储结构
- 图的存储结构
- 图的存储结构
- 图的存储结构
- 图的存储结构
- 图的存储结构
- 【图的存储结构】
- 图的存储结构
- 图的存储结构
- 图的存储结构
- 图的存储结构
- 图的存储结构
- 图的存储结构
- 图的存储结构
- 图的存储结构
- 图的存储结构
- 如何上传本地代码到github上
- ubuntu14.04配置Tensorflow环境
- 大写字符移位_腾讯实习编程题
- centos之lamp环境搭建
- 【hadoop】win7下通过intellij idea对hadoop2.7.3进行访问操作的实践
- 图的存储结构再总结
- 数据结构实验之链表七:单链表中重复元素的删除
- .net实现银联在线支付
- CF AIM Tech Round 3 (Div. 2) D - Recover the String
- IO
- 省身
- 生成学习算法之朴素贝叶斯算法
- [组合] BZOJ 2916 [Poi1997]Monochromatic Triangles
- 数据结构实验之链表二:逆序建立链表