如何写链表代码

35 阅读2分钟

指针

将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指针,或者反过来说,指针中存储了这个变量的内存地址,指向了这个变量,通过指针就能找到这个变量。

在编写链表代码的时候,我们经常会有这样的代码:p->next=q。这行代码是说,p 结点中的 next 指针存储了 q 结点的内存地址。

利用哨兵简化实现难度

其实没有很理解 哨兵(sentinel)是个哑元节点(dummy node),可以简化边界条件,使代码更紧凑,但对速度并没有什么帮助。

哨兵元素的应用总结_w453908766的博客-CSDN博客_哨兵元素

哨兵节点 - 轻轻的吻 - 博客园 (cnblogs.com)

链表中的哨兵是怎么一个作用? - 知乎 (zhihu.com)

无哨兵的情况

对于单链表,如果在结点 p 后面插入一个新的结点new_node,只需要:

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

但是当我们要向一个空链表中插入第一个结点,上面的逻辑就不能用了。

我们需要进行下面这样的特殊处理,其中 head 表示链表的头结点。所以,从这段代码,我们可以发现,对于单链表的插入操作,第一个结点和其他结点的插入逻辑是不一样的。

if (head == null) {
  head = new_node;
}

如果要删除结点 p 的后继结点,我们只需要一行代码就可以搞定。

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

但是,如果我们要删除链表中的最后一个结点,(链表中只剩下一个结点,并不是指尾结点),前面的删除代码就不行了。跟插入头节点类似,我们也需要对于这种情况特殊处理。写成代码是这样子的:

if (head->next == null) {
   head = null;
}

引入哨兵

引入哨兵,解决“边界问题”的,不直接参与业务逻辑。

如果我们引入哨兵结点,在任何时候,不管链表是不是空,head 指针都会一直指向这个哨兵结点。我们也把这种有哨兵结点的链表叫带头链表。相反,没有哨兵结点的链表就叫作不带头链表。

哨兵结点是不存储数据的。因为哨兵结点一直存在,所以插入第一个结点和插入其他结点,删除最后一个结点和删除其他结点,都可以统一为相同的代码实现逻辑了。

image.png

此文章为2月Day01学习笔记,内容来源于极客时间《数据结构与算法之美