数据结构——图—概念和存储(邻接矩阵,邻接表)

图的概念


为什么要有图

在学习图之前我们应该学习了,线性表和树;但是我们有没有考虑过为什么要有图,线性表和图的局限性优势上面呢?
线性表他仅仅局限于一个直接前驱和一个直接后继的关系。
树呢?树也仅仅只能有一个直接前驱也就是父节点。

那么这种多对多的关系怎么处理么? 这里我们就用到了图

图的概念


这里写图片描述
上面图片就是一个图
在线性表中我们把数据元素叫做元素,在树种叫做结点,在图中我们把他叫做顶点(Vertex);
两个顶点之间的连线叫做边;
有方向的叫做有向边,没方向的叫做无向边;
边上的数字代表边的权值,当图上有权值时他也叫做网
这些基本概念足以看懂一个图了那么上面怎么储蓄一个图呢?


图的储存-邻接矩阵


邻接矩阵的概念

邻接矩阵,它是什么形式呢?
形式就是我上面给书的图旁边的那个图;

首先 把图中的各个顶点排成一个行列式
有边的写1(或者边上的权值)没有的写无穷大(现在的图中我们不考虑顶点和顶点自己边)。
为什么要无穷大而不取0 呢因为有的边上的权值可能是0

邻接矩阵的定义


/*
这里首先定义一个vexs数组来存放各个顶点,然后建立arc以顶点数为行和列的行列式 
vexs数组也就是没有显示出来实际行列式的第0行和第0列
*/
typedef struct{

    char vexs[MAXSIZE];//顶点表
    int arc[MAXSIZE][MAXSIZE];//领接矩阵可以看成边表
    int N_v,N_e ;  //定义顶点数字和边数

}M_g;


邻接矩阵创建


/*
这个函数大概意思
就是首先接收到顶点数个边数,用顶点数为矩阵的长宽来初始化矩阵
然后接收某两个顶点之间的关系,改变矩阵中的默认值
*/
void onCreateM_g(M_g *x)
{
    int i,j,k,w;
    printf("请输入顶点数和边数\n");
    scanf("%d%d",&x->N_v,&x->N_e);
    printf("请依次各输入顶点\n");
    for(i=0;i < x->N_v;i++)
    {
        scanf("%d",&x->vexs[i]);
//        puts("sdsdsds");

    }
//
    for(i = 0;i < x->N_v;i++){
       for(j = 0;j < x->N_v;j++)
        {
          x->arc[i][j] = FALL;

        }
    }
    for( k = 0; k < x->N_e;k++)
          {
              printf("请输入(vi,vj)的上标i,下标j 和权 w \n");
              scanf("%d%d%d",&i,&j,&w);
              x->arc[i][j] = w;
              x->arc[j][i] = x->arc[i][j];

          }

}

实现领接矩阵


#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
#define FALL 99999
//用99999来代表无穷大

/*
这里首先定义一个vexs数组来存放各个顶点,然后建立arc以顶点数为行和列的行列式 
vexs数组也就是没有显示出来实际行列式的第0行和第0列
*/
typedef struct{

    char vexs[MAXSIZE];//顶点表
    int arc[MAXSIZE][MAXSIZE];//领接矩阵可以看成边表
    int N_v,N_e ;  //定义顶点数字和边数

}M_g;
//实现
void onCreateM_g(M_g *x)
{
    int i,j,k,w;
    printf("请输入顶点数和边数\n");
    scanf("%d%d",&x->N_v,&x->N_e);
    printf("请依次各输入顶点\n");
    for(i=0;i < x->N_v;i++)
    {
        scanf("%d",&x->vexs[i]);
//        puts("sdsdsds");

    }
//
    for(i = 0;i < x->N_v;i++){
       for(j = 0;j < x->N_v;j++)
        {
          x->arc[i][j] = FALL;

        }
    }
    for( k = 0; k < x->N_e;k++)
          {
              printf("请输入(vi,vj)的上标i,下标j 和权 w \n");
              scanf("%d%d%d",&i,&j,&w);
              x->arc[i][j] = w;
              x->arc[j][i] = x->arc[i][j];

          }

}
void  ergodic (M_g *x)
{   int i,j;
    for(i=0;i< x->N_v;i++)
    {
        for(j=0;j<x->N_v;j++)
        {
             printf("%d\t",x->arc[i][j]);
        }
        putchar('\n');
    }



}
int main(){
    M_g *p;
    p = (M_g*)malloc(sizeof(M_g));
    onCreateM_g(p);
    ergodic (p);

return 0;

}


/*
4 4
0 1 2 3
0 1 56
0 2 34
0 3 78
2 3 25
*/
//复制上面的输入进去查看输出

图的储存-邻接表


图b

邻接表的基本概念

1.为什么要有邻接表
可以想象如果有一个图他的顶点特别多而边只有一个?那么是不是用邻接矩阵特别浪费内存呢?
所以需要邻接表(不同于邻接表主要以链表形式实现)
2.怎么理解邻接表呢?
首先看上面b图,最边上的表我们给他起名——顶点表;其他的我们给另外的变我们给他起名边表;
3.顶点表
顶点表用来开始储存图中的各个顶点,可以看到我们给顶点表开辟了两个域,第一个域存放了顶点了第二个域呢?
我们可以看出来顶点表的第二个域指向了边表,所以在顶点表的第二个域里面我们存放边表类型让他变成边表的头结点指向边表;
4.边表
上图中边表有两个域一个存放和顶点边中有关系的顶点,另外一个也就是他的NEXT域和链表一样在后面没有时他指向为空,其实还可以给他加第三个域比如权值等等这个很简单了
这样各个就形成了一个邻接表了


邻接表的实现


首先是定义这个结构体类型

/*
下面代码的总体意思就是先定义边表我们给了他3个域分别是顶点域 权值域和next域
在定义顶点表 他有两个域分别是顶点域和代表边表头结点的first_v域
最后我们定义一个顶点表的顺序表类型。类似于图b中最左边的那个顺序表
这样一个邻接表就定义好了
*/


typedef int VertexType;/*顶点类型有用户定义*/
typedef struct EdgeNode//定义边表
{
    int adjvex;//邻接点域 里面放各个和顶点表对应节点有关的顶点
    int weight;//权值
    struct EdgeNode *next;

}EdgeNode;
typedef struct VertexNode /*定义顶点表*/
{
    VertexType data;//顶点域,存储顶点信息
     EdgeNode *first_v;//边表的头指针
}VertexNode;
typedef struct   //邻接表
{
   VertexNode adjlist[M]; //用于存放头结点和顶点的顺序表;
   int n_v,n_e;

}LinkdeGraph;

创建邻接表


/*
下面代码的主要含义是
1.先获取到图中的顶点数和边数,然后以顶点数为最大创建顶点表数组并且初始化
2.然后定义一个边表类型,输入某个边上的两个顶点,并且用头插发插入
边的表示(vi vj)表示连接vi vj的边
3.函数解释
大概意思就是接收到边上的两个顶点后,把和i有边的j节点用头插发插在顶点表中有i顶点那一段之后;

void onCreate_L(LinkdeGraph *p,int c)//等于0表示建立为无向图
{
    int i,j,k;
    EdgeNode *e;
    printf("请输入顶点数和边数:\n");
    scanf("%d%d",&p->n_v,&p->n_e);
    printf("请依次输入顶点\n");
    for(i = 0; i < p->n_v; i++ )//输入顶点信息,建立顶点表

    {
        scanf("%d",&p->adjlist[i].data);//输入顶点信息
        p->adjlist[i].first_v = NULL;   //初始状态默认边表为空

    }

    for(k = 0 ; k < p->n_e;k++)//建立边表
    {
        printf("请输入边(vi,vj)上的顶点序号:\n");
        scanf("%d%d",&i,&j);//输入边vivj上的顶点序号
        e = (EdgeNode*)malloc(sizeof(EdgeNode));//开辟边表的空间
        /*
                头插法
        */
        e->adjvex = j; //顶点为j
        e->next = p->adjlist[i].first_v;//将边表e作为头指针放进顶点表的first域
        p->adjlist[i].first_v = e;
        if(c == 0)
            {
                 e = (EdgeNode*)malloc(sizeof(EdgeNode));
                 e ->adjvex = i;
                 e->next = p->adjlist[j].first_v;
                 p->adjlist[j].first_v = e;

            }


    }



}

实现领接表


#include<stdio.h>
#include<stdlib.h>
#define M 20
typedef int VertexType;/*顶点类型有用户定义*/
typedef struct EdgeNode//定义边表
{
    int adjvex;//邻接点域 里面放各个和顶点表对应节点有关的顶点
    int weight;//权值
    struct EdgeNode *next;

}EdgeNode;
typedef struct VertexNode /*定义顶点表*/
{
    VertexType data;//顶点域,存储顶点信息
     EdgeNode *first_v;//边表的头指针
}VertexNode;
typedef struct   //邻接表
{
   VertexNode adjlist[M]; //用于存放头结点和顶点的顺序表;
   int n_v,n_e;

}LinkdeGraph;
//创建邻接表
void onCreate_L(LinkdeGraph *p,int c)//等于0表示建立为无向图
{
    int i,j,k;
    EdgeNode *e;
    printf("请输入顶点数和边数:\n");
    scanf("%d%d",&p->n_v,&p->n_e);
    printf("请依次输入顶点\n");
    for(i = 0; i < p->n_v; i++ )//输入顶点信息,建立顶点表

    {
        scanf("%d",&p->adjlist[i].data);//输入顶点信息
        p->adjlist[i].first_v = NULL;   //初始状态默认边表为空

    }

    for(k = 0 ; k < p->n_e;k++)//建立边表
    {
        printf("请输入边(vi,vj)上的顶点序号:\n");
        scanf("%d%d",&i,&j);//输入边vivj上的顶点序号
        e = (EdgeNode*)malloc(sizeof(EdgeNode));//开辟边表的空间
        /*
                头插法
        */
        e->adjvex = j; //顶点为j
        e->next = p->adjlist[i].first_v;//将边表e作为头指针放进顶点表的first域
        p->adjlist[i].first_v = e;
        if(c == 0)
            {
                 e = (EdgeNode*)malloc(sizeof(EdgeNode));
                 e ->adjvex = i;
                 e->next = p->adjlist[j].first_v;
                 p->adjlist[j].first_v = e;

            }


    }



}
int main(){
    int c = 0;
    LinkdeGraph *p;
    p = (LinkdeGraph*)malloc(sizeof(LinkdeGraph));
    onCreate_L(p,c);


return 0;

}

有问题可私聊
传送门 图的遍历(广度优先——深度优先遍历)http://blog.csdn.net/qq_33329316/article/details/53573798

  • 26
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值