一. 单链表
int head, idx; //head存储链表头,idx表示当前模拟到了哪个结点
int e[N], ne[N]; //e[]存储数据域的值,ne[]存储结点的next指针
初始化
为空链表,所以:
void init()
{
head = -1;
idx = 0;
}
存储元素
在表头插入一个数x
void add_to_head(int x)
{
e[idx] = x;
ne[idx] = head;
head = idx ++;
}
删除头结点
删除前,head = 0;ne[0] = 1;
若想删除头结点a1,只需让head指针直接指向a2的下标1即可,即head = ne[head]
void remove()
{
head = ne[head];
}
插入
在下标为k的点后面插入一个点x
例:将a6插入到a2后面。
- 首先将a6的值存起来
- 将a6的next域指向a3
- 将a2的next域指向a6
void add(int k, int x)
{
e[idx] = x;
ne[idx] = ne[k]; //ne[5] = ne[1] = 2;
ne[k] = idx ++; //ne[1] = 5;
}
删除
删除下标为k的点后面的点
例: 若删除a3,就直接让a2的next域指向a4,跳过a3。即ne[1] = ne[ne[1]] = 3
;
void delete(int k)
{
ne[k] = ne[ne[k]];
}
二. 树和图的存储
- 树是一种特殊的图,所以树的存储可以当成图的存储来做。
- 图的存储可以分为邻接表和邻接矩阵两种方式。
Ⅰ. 邻接表
对于上图的邻接表,把每一行看成是一个单链表,其中每一个单链表存储的是一个点可以到的其它点的集合。
// 对于每个点k,开一个单链表,存储k所有可以走到的点。h[k]存储这个单链表的头结点,e[]存储数据域的值,ne[]存储结点的next指针
int h[N], e[N], ne[N];
int idx; //idx表示当前的起点模拟到了哪个结点
初始化
只有n个点,没有边,所以初始化时:
idx = 0;
memeset(h, -1, sizeof(h));
将边加入图或树中
对于每一个单链表,插入一条从a到b的边:
可以把每一行看成是一个单链表,每一个单链表的头结点就是h[a]。当发现有从a到b的边时,就变相的是向头结点为a的单链表的头那儿中插入一个点b,所以就把单链表的存储那儿的代码的head换成h[a]就好了。
//添加一条边:a -> b
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
Ⅱ. 邻接矩阵
如上图的有向带权图,直接可以用二维数组来模拟存储过程:
int g[N][N]; //g[i][j]: 从点i到点j的距离
int n, m; //n个点,m条边
int a, b, c; //点a,b, 权重为c
int main()
{
cin >> n >> m;
//初始化数组
memset(g, 0x3f3f3f3f, sizeof(g));
for(int i = 1; i <= m; i++)
{
cin >> a >> b >> c;
g[a][b] = c;
}
}
对于无向图中的边ab,存储时存储两条有向边a->b, b->a
g[a][b] = g[b][a] = c;