关于存图(树)

先前一直都是直接敲模板,没有理会原理,今天稍微整理一下.


  • 邻接矩阵:二维数组存图

  • 邻接表:对于每一个结点开一个链表存储与该节点相关的信息。
    存图时习惯表述的邻接表一种是存点,一种是存边(此即链式前向星)。对于存点的情况,如果需要存储相应边的信息,可以开一个结构体或者 \(stl\) \(pair\)同时记录边的权值等,代码中一般用 \(vector<pair<int,int>>\) 来实现 。

  • 前向星:以储存边的方式来存储图。记录好所有边,按起点排好序,然后用 \(head[x]\) 记录以 \(x\) 为起点的第一条边。

  • 链式前向星:
    可以发现上述前向星的时间复杂度瓶颈是排序,因此用链表对存储过程进行优化。同时可以发现前向星排好序后的结果,就是对于每一个点存储所有连出的边。因此用邻接表实现前向星的存储,此即链式前向星。
    代码中一般用数组模拟链表。对于每一个结点开一个链表,链表共四个数组 \(head,ver,nxt,edge\),其中 \(head\) 作为表头数组,\(head[x]\) 记录结点 \(x\) 链表的头指针,\(ver[i]\) 记录输入时索引为 \(i\) 的边指向的结点 \(y\)\(nxt[i]\) 记录输入时索引为 \(i\) 的边指向的下一条该节点对应边的索引 \(j\)\(edge[i]\) 记录边 \(i\) 的权值。
    存边时的具体优化进程:\(tot\) 是边的物理索引,没什么关系。加入一条新边 \(x\rightarrow y\) 时,把它塞到 \(i=head[x]\)\(nxt[i]\) 之间,时间复杂度为 \(O(1)\)

  • 成对变换:
    用到异或性质:\(x\) 为奇数时,\(x\) xor \(1\) = \(x-1\)\(x\) 为偶数时,\(x\) xor \(1=x+1\)。换成二进制看。
    在存图中的应用:存储无向边实际上是存两条有向边,令存储第一条边的物理下标 \(tot\)\(0\),则可由一条边的下标迅速对应到相反边的下标。具体地,若 \(ver[i]\) 表示第 \(i\) 条边的起点,则 \(ver[i\) xor \(1]\) 表示其终点。


当然搞明白了以后最好当模板熟练背下来,不要浪费时间再想怎么敲出链式前向星。注意加边是链表头插

void add_E(int x,int y){ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;}
------
for(int i=head[x];i;i=nxt[i])
	{
		int y=ver[i];
		,,,
	}

链表下标不要有 \(0\)!!!否则之后遍历会错!!!

posted @ 2025-01-08 21:06  Freshair_qprt  阅读(6)  评论(0编辑  收藏  举报