第三章 链表和list 双向链表部分
该部分与单链表部分基本相同,但是增加了prev指针域,即双向链表。
实现方式
依旧采用静态实现。双向链表就是在单链表的基础上加上一个前驱指针,增加一个数组充当前驱指针。
本部分与上部分基本相同,将单链表理解透彻后,双向链表就不难理解了。
基本定义
const int N = 100010;
int h; /* 头指针 */
int e[N]; /* 数据域 */
int ne[N]; /* 指针域 */
int id; /* 分配地址给新结点 */
int prev[N]; /* 新增:前驱指针 */
int mp[N]; /* 标记各值位置的数组 */
头插
void push_front(int x) {
id++;
e[id] = x;
mp[x] = id;
ne[id] = ne[h];
prev[id] = h;
prev[ne[h]] = id;
ne[h] = id;
}
时间复杂度:O(1)
遍历链表
这里无需prev数组,与单链表相同。
void print() {
for (int i = ne[h]; i ; i = ne[i]) {
cout << e[i] << " ";
}
}
时间复杂度:O(n)
按值查找
直接利用标记数组即可。
int find(int x) {
return mp[x];
}
时间复杂度:O(1)
在任意位置后插入元素
void insert_back(int p, int x) {
id++;
e[id] = x;
mp[x] = id;
ne[id] = ne[p];
prev[id] = p;
prev[ne[p]] = id;
ne[p] = id;
}
时间复杂度:O(1)
在任意位置前插入元素
void insert_front(int p, int x) {
id++;
e[id] = x;
mp[x] = id;
ne[id] = p;
prev[id] = prev[p];
ne[prev[p]] = id;
prev[p] = id;
}
时间复杂度:O(1)
删除任意位置的元素
void del(int p) {
if (p) {
mp[e[p]] = 0;
prev[ne[p]] = prev[p];
ne[prev[p]] = ne[p];
}
}
时间复杂度:O(1)
在此再次提醒,无论是e[]
还是ne[]
抑或是prev[]
,括号内的内容即下标代表的都是结点的物理地址,而不是逻辑地址。