写出正确的链表代码技巧

小心指针丢失

以单链表插入为例:

image.png

错误的代码:

p->next = x;  // 将p的next指针指向x结点;
x->next = p->next;  // 将x的结点的next指针指向b结点;

p->next 指针在完成第一步操作之后,已经不再指向结点 b 了,而是指向结点 x。第 2 行代码相当于将 x 赋值给 x->next,则是自己指向自己。

在插入结点时,一定要注意操作顺序,要先将结点 x 的 next 指针指向结点 b,再把结点 a 的 next 指针指向结点 x,这样才不会丢失指针,导致内存泄漏。

x->next = p->next;  // 将x的结点的next指针指向b结点;
p->next = x;  // 将p的next指针指向x结点;

利用哨兵简化实现难度

上面在结点 p 后面插入一个新的结点的代码,在链表为空时,就不起作用了。

if (p == NULL)
{
    p = new node();
}

再来看看是删除操作,如果要删除后继结点。

p->next = p->next->next;

但是,如果我们要删除链表中的最后一个结点,前面的删除代码就不 work 了。跟插入类似,我们也需要对于这种情况特殊处理。

如果我们引入哨兵结点,在任何时候,不管链表是不是空,head 指针都会一直指向这个哨兵结点。我们也把这种有哨兵结点的链表叫带头链表。相反,没有哨兵结点的链表就叫作不带头链表。我画了一个带头链表,你可以发现,哨兵结点是不存储数据的。因为哨兵结点一直存在,所以插入第一个结点和插入其他结点,删除最后一个结点和删除其他结点,都可以统一为相同的代码实现逻辑了。

image.png

留意边界处理
  • 如果链表为空,代码是否能正常工作?
  • 如果链表只包含1个结点,代码是否能正常工作?
  • 链表只包含2个结点,代码是否能正常工作?
  • 代码逻辑再处理头结点和尾结点,代码是否能正常工作?

练习:
  • 单链表反转

  • 链表中环的检测
    快慢指针

  • 删除链表倒数第n个结点
    起点不同,first 和 first+n

  • 求链表的中间结点

    快慢指针,用两个指针 slow 与 fast 一起遍历链表。slow 一次走一步,fast 一次走两步。那么当 fast 到达链表的末尾时,slow 必然位于中间。

  • 2个有序链表合并

  • 求链表相交结点

  • 判断链表回文

你可能感兴趣的:(写出正确的链表代码技巧)