Appearance
题目
已知一个带有表头结点的双向循环链表 L,结点结构为 prev|data|next,prev 和 next 分别是指向其直接前驱和直接后继结点的指针。现要删除指针 p 所指的结点,正确的语句序列是( )。
错因
A
第 1 句对:p->next->prev = p->prev 让 p 的后继的 prev 指向 p 的前驱,没问题。但第 2 句错:p->prev->next = p->prev 让 p 的前驱的 next 指向自己(前驱→前驱自环),应该指向 p 的后继。错因是写第 2 句时机械对称地照抄了第 1 句的右值 p->prev,没意识到两句的方向是相反的。
B
第 1 句错:p->next->prev = p->next 让 p 的后继的 prev 指向自己(后继→后继自环),应该指向 p 的前驱。第 2 句对。这是 A 的对称错误:选 B 的人写第 1 句时把右值错抄成了 p->next。常见的"对称对仗"心理:以为两句赋值的右值要分别配 next 和 prev,结果方向颠倒。
C
两句都错,且都让"邻居指向自己"。p->next->prev = p->next 让后继的 prev 自指,p->prev->next = p->prev 让前驱的 next 自指。错因是没理解删除的本质——把 p 的前驱和后继直接相连绕过 p。选 C 的人可能死记"修改 prev 和 next 字段",却把右值都错填成了"自己的指针",结果 p 的前后两个邻居都被搞成自环。
总解析
删除的核心思路:让 p 的前驱和后继直接相连,绕过 p,再 free(p)。
| 改谁 | 改成什么 | 含义 |
|---|---|---|
p->next->prev | p->prev | "p 后继"的前驱,应是"p 的前驱" |
p->prev->next | p->next | "p 前驱"的后继,应是"p 的后继" |
free(p) | — | 释放 p 占用的空间,必须放在最后 |
为什么前两句的顺序无所谓:双向链表里 p 自身的 prev 和 next 字段一直保留到 free 之前,所以无论先改 p->next->prev 还是 p->prev->next,访问 p->prev、p->next 都不会失效。但 free(p) 必须放最后——一旦释放,再访问 p 就是悬空指针,会崩。
对比:单链表删除时由于只能向前找前驱,写法上有先后讲究;双向链表两边都能直接拿到,所以前两句顺序自由。
用图直观验证:
原状态:... ↔ A(p->prev) ↔ p ↔ B(p->next) ↔ ...
操作后:... ↔ A ↔ B ↔ ...,p 被释放。
- A 的 next 字段:原指 p,新指 B —— 即
p->prev->next = p->next✓ - B 的 prev 字段:原指 p,新指 A —— 即
p->next->prev = p->prev✓
最终答案是 D。