指针
将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指针,或者反过来说,指针中存储了这个变量的内存地址,指向了这个变量,通过指针就能找到这个变量。
在编写链表代码的时候,我们经常会有这样的代码: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 指针都会一直指向这个哨兵结点。我们也把这种有哨兵结点的链表叫带头链表。相反,没有哨兵结点的链表就叫作不带头链表。
哨兵结点是不存储数据的。因为哨兵结点一直存在,所以插入第一个结点和插入其他结点,删除最后一个结点和删除其他结点,都可以统一为相同的代码实现逻辑了。
此文章为2月Day01学习笔记,内容来源于极客时间《数据结构与算法之美》