SDUT3117图的基本存储的基本方式一 二 三 四

Part 一 、 二、
邻接矩阵 与邻接表(邻接链表)

图的基本存储的基本方式二

Time Limit: 1000 ms Memory Limit: 65536 KiB

Problem Description
解决图论问题,首先就要思考用什么样的方式存储图。但是小鑫却怎么也弄不明白如何存图才能有利于解决问题。你能帮他解决这个问题么?

Input
多组输入,到文件结尾。
每一组第一行有两个数n、m表示n个点,m条有向边。接下来有m行,每行两个数u、v代表u到v有一条有向边。第m+2行有一个数q代表询问次数,接下来q行每行有一个询问,输入两个数为a,b。

注意:点的编号为0~n-1,2<=n<=500000 ,0<=m<=500000,0<=q<=500000,a!=b,输入保证没有自环和重边

Output
对于每一条询问,输出一行。若a到b可以直接连通输出Yes,否则输出No。
Sample Input
2 1
0 1
2
0 1
1 0
Sample Output
Yes
No
Hint

Source
lin
以下该think总结转载于xxx博主 链接, 用以学习 。
还有学姐 xxx 链接
THINK:图的表示
(1)邻接矩阵
用下标代表点的标号,以二维数组储存数值表示边的存在与否,或者边的长度大小。
(2 )邻接表
一、邻接表
邻接表是图的一种链式存储结构。
邻接表中,对图中每个顶点建立一个单链表,第i个单链表中的结点表示依附于顶点Vi的边(对有向图是以顶点Vi为尾的弧)。
邻接表的处理方法是这样的:
(1)图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过,数组可以较容易的读取顶点的信息,更加方便。
(2)图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以,用单链表存储,无向图称为顶点vi的边表,有向图则称为顶点vi作为弧尾的出边表。
这里写图片描述
二、无向图的邻接表
这里写图片描述
三、有向图的邻接表和逆邻接表
(一)在有向图的邻接表中,第i个单链表链接的边都是顶点i发出的边。
(二)为了求第i个顶点的入度,需要遍历整个邻接表。因此可以建立逆邻接表。
(三)在有向图的逆邻接表中,第i个单链表链接的边都是进入顶点i的边。
这里写图片描述
另外,十字链表:
对于有向图来说,邻接表是有缺陷的。入度和出度问题不能同时解决。十字链表,就是把邻接表和逆邻接表结合起来的,这样既容易找到以v为尾的弧,也容易找到以v为头的弧,因而比较容易求得顶点的出度和入度。

四、邻接表小结
◆ 设图中有n个顶点,e条边,则用邻接表表示无向图时,需要n个顶点结点,2e个表结点;用邻接表表示有向图时,若不考虑逆邻接表,只需n个顶点结点,e个边结点。
◆ 在无向图的邻接表中,顶点vi的度恰为第i个链表中的结点数。
◆ 在有向图中,第i个链表中的结点个数只是顶点vi的出度。在逆邻接表中的第i个链表中的结点个数为vi的入度。
◆ 建立邻接表的时间复杂度O(n+e)。
五、两者使用途径的区别:
如果图中边的数目远远小于n2称作稀疏图,这是用邻接表表示比用邻接矩阵表示节省空间;
如果图中边的数目接近于n2,对于无向图接近于n*(n-1)称作稠密图,考虑到邻接表中要附加链域,采用邻接矩阵表示法为宜。

在空间允许情况下:
在有向图中求顶点的度采用邻接矩阵比采用邻接表表示更方便

邻接表表示中第i个边表上的结点个数就是顶点Vi的出度,求入度较困难,需遍历个顶点的边表
逆邻接表表示中第i个边表上的结点个数就是顶点Vi的入度,求出度较困难,需遍历个顶点的边表

在邻接矩阵中求边的数目必须检测整个矩阵,所消耗的时间是O(n)
在邻接表中求边的个数,只要对每个边表计数即可求得所消耗的时间是O(n+e)

附加:简易的邻接表储存图的oj 题。代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node //数据太大,不能用二维数组
{
    int data;
    struct node *next;
}node;
node *a[500000], *p;
int main()
{
    int n,m,i,u,v,q;
    while(~scanf("%d %d", &n, &m))
    {
        for(i=0; i < 500000; i++)
            a[i] = NULL; //初始化节点
        while(m--)
        {
            scanf("%d %d", &u, &v);
            if(a[u] == NULL)
            {
                p = (node *)malloc(sizeof(node)); //第一次连,直接连
                p->data = v;
                p->next = NULL;
                a[u] = p;
            }
            else
            {
                p = (node *)malloc(sizeof(node)); //已经连通,插入,
                p->data = v;
                p->next = a[u]->next;
                a[u]->next = p;
            }
        }
        scanf("%d",&q);
        while(q--)
       {
        int flag = 0;
        scanf("%d %d", &u, &v);
        p = a[u];
        while(p != NULL)
        {
            if(p->data == v)
            {
                flag = 1;
                break;
            }
            p = p->next;
        }
        if(flag == 1)
            printf("Yes\n");
            else
                printf("No\n");
       }
    }
        return 0;
}


/***************************************************
User name: i am your lover
Result: Accepted
Take time: 56ms
Take Memory: 5988KB
Submit time: 2018-08-
****************************************************/

图的基本存储的基本方式一
Time Limit: 1800 ms Memory Limit: 65536 KiB
Input
多组输入,到文件结尾。

每一组第一行有两个数n、m表示n个点,m条有向边。接下来有m行,每行两个数u、v代表u到v有一条有向边。第m+2行有一个数q代表询问次数,接下来q行每行有一个询问,输入两个数为a,b。

注意:点的编号为0~n-1,2<=n<=5000 ,n*(n-1)/2<=m<=n*(n-1),0<=q<=1000000,a!=b,输入保证没有自环和重边

Output
对于每一条询问,输出一行。若a到b可以直接连通输出Yes,否则输出No。

Sample Input
2 1
0 1
2
0 1
1 0
Sample Output
Yes
No
Hint
Source
lin
临街矩阵

#include <stdio.h>
#include <string.h>

bool map[5000][5000];//标记路径,bool类型在存储二值变量,
//或者说只有真假时,更具优势,
//因为只有0和1即false和true,省空间
int main()
{
    int n, m, u, v;
    while(~scanf("%d%d",&n,&m))
    {
        memset(map, 0, sizeof(map));
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d",&u,&v);
            map[u][v] = 1;
        }
        int q;
        scanf("%d",&q);
        while(q--)
        {
            int a, b;
            scanf("%d%d",&a,&b);
            if(map[a][b])
               {
                printf("Yes\n");
               }
           else  printf("No\n");
        }
    }
    return 0;
}

part 3. 三 结构体+数组(边集数组)

图的基本存储的基本方式三

Time Limit: 1000 ms Memory Limit: 65536 KiB

Problem Description
解决图论问题,首先就要思考用什么样的方式存储图。但是小鑫却怎么也弄不明白如何存图才能有利于解决问题。你能帮他解决这个问题么?

Input
多组输入,到文件结尾。
每一组第一行有两个数n、m表示n个点,m条有向边。接下来有m行,每行两个数u、v、w代表u到v有一条有向边权值为w。第m+2行有一个数q代表询问次数,接下来q行每行有一个询问,输入一个数为a

注意:点的编号为0~n-1,2<=n<=500000 ,0<=m<=500000,0<=q<=500000,u!=v,w为int型数据。输入保证没有自环和重边

Output
对于每一条询问,输出一行两个数x,y。表示排序后第a条边是由x到y的。对于每条边来说排序规则如下:
权值小的在前。

权值相等的边出发点编号小的在前

权值和出发点相等的到达点编号小的在前
注:边的编号自0开始

Sample Input
4 3
0 1 1
1 2 2
1 3 0
3
0
1
2
Sample Output
1 3
0 1
1 2
Hint

Source
lin
了解下qsort函数
C/C++中有一个快速排序的标准库函数 qsort ,在stdlib.h 中声明,其原型为:
void qsort(void base, int nelem, unsigned int width, int ( pfCompare)( const void , const void ));

  使用该函数,可以对任何类型的一维数组排序。该函数参数中,base 是待排序数组的起始地址,nelem 是待排序数组的元素个数,width 是待排序数组的每个元素的大小(以字节为单位),最后一个参数 pfCompare 是一个函数指针,它指向一个“比较函数”。排序就是一个不断比较并交换位置的过程。qsort 如何在连元素的类型是什么都不知道的情况下,比较两个元素并判断哪个应该在前呢?答案是,qsort 函数在执行期间,会通过pfCompare指针调用一个 “比较函数”,用以判断两个元素哪个更应该排在前面。这个“比较函数”不是C/C++的库函数,而是由使用qsort 的程序员编写的。在调用qsort 时, 将“比较函数”的名字作为实参传递给pfCompare。程序员当然清楚该按什么规则决定哪个元素应该在前,哪个元素应该在后,这个规则就体现在“比较函数”中。

qsort 函数的用法规定,“比较函数”的原型应是:int 函数名(const void * elem1, const void * elem2);该函数的两个参数,elem1 和elem2,指向待比较的两个元素。也就是说, * elem1 和* elem2 就是待比较的两个元素。该函数必须具有以下行为:

  1) 如果 * elem1 应该排在 * elem2 前面,则函数返回值是负整数(任何负整数都行)。

  2) 如果 * elem1 和* elem2 哪个排在前面都行,那么函数返回0

  3) 如果 * elem1 应该排在 * elem2 后面,则函数返回值是正整数(任何正整数都行)。

以下代码原作者忘了谁了 哈哈哈
以下为AC 代码 当做学习整理

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Max 591000
struct node
{
    int u;//记录端点
    int v;//记录端点
    int w;//记录权值
}s[500010];
int cmp(const void *a, const void *b)
{
    struct node *c = (struct node *)a;
    struct node *d = (struct node *)b;
    if(c->w != d->w) return c->w - d->w;
    else if (c->u != d->u)
        return c->u - d->u;
    else
        return c->v - d->v;
}
int main()
{
    int i,n,m,t,q;
    while(~scanf("%d %d",&n,&m))
    {
        memset(s,Max, sizeof(struct node)); //对数组初始化防止影响结果
        for(i = 0; i < m; i++)
        {
            scanf("%d %d %d",&s[i].u, &s[i].v, &s[i].w);
        }
        qsort(s,m,sizeof(s[0]),cmp);
        scanf("%d",&q);
        while(q--)
        {
            scanf("%d",&t);
            printf("%d %d\n",s[t].u, s[t].v);
        }
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值