数组模仿单链表与邻接矩阵
1.定义
数组不仅仅具体存储的数据是有用的,有时候它的下标同样能够提供非常有用的信息,这一点常常被我们忽略。对于一个链表结构,最基本的元素有节点与next指针,对于每个节点可以用不同的
i
d
x
i
idx_i
idxi唯一标识,它所拥有的数据可以使用e[idx]表示,而它的指针则可以用ne[idx]来表示,指向对应的
i
d
x
j
idx_j
idxj:
e
[
i
]
=
d
a
t
a
i
n
e
[
i
]
=
j
e[i] = data_i\\ne[i] = j
e[i]=dataine[i]=j
首先我们开辟能完全满足数据范围的数组们(一般为1e6):
const int N = 100010;
int val[N],ne[N],idx; //表明最多有N个节点,N条边
2.链表结构
单链表常常使用一个没有意义的空节点作为头Head,这样可以有效减少头插法的难度。
如下图所示,当新的节点4要插入头部的时候,只需要head的next指针指向4,将新节点的next指针指向1即可。所以不妨只用一个变量head标识,而head存储的值为head指向的节点idx。
对于头插法,可以采取如下手段(要说明的是idx为一个全局变量,标识着一个新的空节点id,若该新节点被拿去使用,只需在此基础上加上一个):
void insert_head(int x)
{
ne[idx] = head; //将原先的边给予新建的节点
val[idx] = x; //付值给当前节点
head = idx++; //将head指向新建的节点,并将idx++,模拟新建一个空节点
}
当然,在此之前应当先进行初始化,将head指向null,即将head=-1:
void init()
{
head = -1;
idx = 0
}
理解了头插法,在指定节点后插入新值则可以照葫芦画瓢:
void insert(int a,int x)
{
ne[idx] = ne[a]; //新节点的边设值
val[idx] = x; //新节点的数据设值
ne[a] = idx; //原先节点指向当前新节点
idx++; //新建一个节点
}
删除指定idx的后一个节点:
void del(int a)
{
ne[a] = ne[ne[a]]; //直接指向next of next
}
遍历链表:
int t = head; // head指向的第一个节点下标为t
for(t; t!= -1 /*t不为nullptr*/;i = ne[i]/*变成下一个节点*/){
cout << e[t];
}
3.邻接表
邻接表的结构,可以看成是多个单链表的集合,h[a]本身代表邻接表中的一个节点,而h[a]存储的数值,则是它所维护单链表的第一个节点,这个单链表记录着与 a相邻的点的下标b。(注意不要把单链表中的节点和邻接表中的节点弄混,这二者是不同的概念!!!!)
void insert(int a,int b)
{
e[idx] = b; //值得注意的是e代表指向的idx
//这儿是头插
ne[idx] = h[a]; //新节点的边设值,指向原先的节点
h[a] = idx++; //当前链表的头指向新节点
}
遍历:
for(int t = h[i]/*取出节点t的链表进行遍历*/;t!=-1;t=ne[t]){
cout << e[t]; //e[t]记录着与i相邻的节点idx
}