图的存储笔记

众所周知,图的存储有三种,分别是:

1.邻接矩阵

2.邻接表

3.链式向前星

邻接矩阵

邻接矩阵就是一个矩阵,比如用一个二维数组 a a a 来存储, a a a[ i i i][ j j j]表示节点 i i i 与节点 j j j 之间有没有边,如果有,权值是多少,比如下面这个有向图(没有权值):

那么它就可以用邻接矩阵表示(0表示没有边,1表示有边):

12345
101000
200100
300000
400001
510100

它的缺点很明显,就是耗内存,存储 n n n 个节点的图空间复杂度是 O ( n 2 ) O(n^2) O(n2) ,优点也很明显,可以用 O ( 1 ) O(1) O(1) 的复杂度来找到指定两个节点之间的边,对于边比较多的还是比较划算的,在节点少的时候是个非常好的选择

邻接表

每一个节点存储与它相连的节点,比较适合排序每一个点,而且节省空间

vector<int>d[NR];//d[i]表示与i相连的点

链式向前星

铺垫

各位都用过链表吧,但是为什么却在OI里直接应用的题目那么少呢?无非就是动态分配内存时间复杂度太高了,所以本蒟蒻想出了一个办法来优化链表,那就是提前分配一个很大的数组的内存,然后这个数组的下标就是我们自己定义的地址,而不是指针,不好解释,代码:

//一般的链表
struct node
{
    int val;//节点信息
    node *next;//下一个节点
    node *last;//上一个节点
};
node *head, *tail;
//改进复杂度后的算法
const int NR=1e6+10;//提前分配的内存大小
struct data
{//存储每个节点的数据
   	//...
};
data a[NR];//提前分配内存
int c;//记录现在前c块内存分配过了
int nxt[NR];//每个节点的下一个节点的索引
int lst[NR];//每个节点的上一个节点的索引
int head, tail;

这样也可以实现链表的效果,比如在 x x x 节点之后插入一个 d a t dat dat 数据,可以写成:

a[++c]=dat;//模拟分配内存
nxt[c]=nxt[x];
lst[c]=x;
lst[nxt[x]]=c;
nxt[x]=c;

删除节点 x x x

nxt[lst[x]]=nxt[x];

时间复杂度都很不错,于是我们就有了这种链表

言归正传,链式向前星呢其实跟这种链表的原理差不多,它是用一个链表来存储以节点 x x x 为起点的边,那么对于有 n n n 个节点的边有 n n n 个链表,对于以节点 x x x 为起点的边的链表头我们表示为 h e a d head head[ x x x] , d a t a data data 用来存储每一条边,所以代码长这样:

struct edge//data改名了
{
    int to, w;
    //终点的节点编号,权值
    //注意这里不需要记录从哪里来,因为每个链表中起点是确定的
}e[MR];//MR边数,作用同上文
int head[NR], c, nxt[MR];//NR是节点数,c的作用同上文
void add(int u, int v, int w)
{//起点,终点,权值
    c++;//分配内存
    e[c].to=u;
    e[c].w=w;
    //边的信息
    nxt[c]=head[u];//在链表头前面添加元素
    head[u]=c;//更新链表头
}

这就是链式向前星,不过其实我更愿意把 n x t nxt nxt 数组换一个写法:

struct edge
{
    int to, w, nxt;
}e[MR];
int c;
int head[NR];
void add(int u, int v, int w)
{
    c++;
    e[c].to=u;
    e[c].w=w;
    e[c].nxt=head[u];
    head[u]=c;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值