浅谈链表的优化技巧
浅谈链表的优化技巧
麻烦写法:
正常写链表,都是一个头元素 head
一个尾元素 tail
来记录链表的虚拟头和虚拟尾或真实头和真实尾。
但是显然在插入操作和删除操作的时候,这种写法就会显得很麻烦,要特判一大堆情况:
- 插入的元素在链表头:更新
head
且只要更新两条关系链。 - 在中间:最正常,没话说,需更新四条关系链:pre → \rightarrow → cur & cur → \rightarrow → pre & cur → \rightarrow → nxt & nxt → \rightarrow → pre。
- 在末尾:更新
tail
且只要更新两条。
给个代码:
void Insert(int i,char ch){++tot;node[tot].ch=ch;if(!h)h=t=tot;else{if(!i){node[tot].nxt=h,node[h].pre=tot;h=tot;}else{int x=Find(i),y=node[x].nxt;node[tot].pre=x;node[tot].nxt=y;if(!y)t=tot;elsenode[y].pre=tot;node[x].nxt=tot;}}++m;return;}void del(int i){if(m==1)h=t=0;else{if(i==1){h=node[h].nxt;node[h].pre=0;}else{if(i==m){t=node[t].pre;node[t].nxt=0;}else{int x=Find(i);int l=node[x].pre,r=node[x].nxt;node[r].pre=l;node[l].nxt=r;}}}--m;return;}
看着就很头疼啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。。。
怎么说。。细节太多了,不好搞。码量大,还容易炸。
优化方法:
为链表添加虚拟的头尾元素。使得真正的链表只在虚拟头之后、虚拟尾之前。
- 初始化时添加,所以链表至少有两个元素
- 操作时不会涉及到虚拟元素,所以可以避免操作链表头尾
怎么样,看着就挺 nice 的。
操作事项:
- 虚拟头的后面就是真正头
- 虚拟尾的后面就是真正尾
- 虚拟头的靠后元素为虚拟尾时,实际链表为空链表
别急——此时还能优化!
有种链表叫做环形链表,即 node[tail].nxt=head
或 node[head].pre=tail
。
那么我们就不用用两个虚拟元素,用一个虚拟元素将链表连成环形。
- 只需要一个虚拟元素,环形链表没有链表头和链表尾
- 操作时不会涉及到虚拟元素,所以链表不可能是空链表
- 虚拟元素的靠后元素就是真实头
- 虚拟元素的考前元素就是实际尾
- 虚拟元素的靠后元素为自己时,实际链表为空
好处呢?
通常用下标为 0 0 0 的元素作为虚拟元素(造福了数组模拟链表党)
- 空指针也用 0 0 0 表示
- 可以访问下标为 0 0 0 的元素,但是不能访问空指针
这时候懒得打代码怎么办?
Copy 一下老师的代码
void Insert(int i, char c) { // 第 i 个字符后插入 ce[++n] = {c}; // 新插入的元素int x = Find(i), y = e[x].nxt; // 找到插入位置 xe[n].prv = x, e[n].nxt = y; // 新元素 n 与两边建立联系e[y].prv = e[x].nxt = n;++m;
}void Delete(int i) {int x = Find(i), l = e[x].prv, r = e[x].nxt;e[r].prv = l, e[l].nxt = r;m--;
}
是不是瞬间清洗许多?
撒花~~