1. 总结
在对链表进行操作时,一种常用的技巧是添加一个哑节点(dummy node),它的 next 指针指向链表的头节点。这样一来,我们就不需要对头节点进行特殊的判断了。
例如,如果我们要删除节点 y,我们需要知道节点 y 的前驱节点 x,并将 x 的指针指向 y 的后继节点。但由于头节点不存在前驱节点,因此我们需要在删除头节点时进行特殊判断。但如果我们添加了哑节点,那么头节点的前驱节点就是哑节点本身,此时我们就只需要考虑通用的情况即可。
总而言之,当处理head节点和处理其余节点需要用到不同做法时(因为head前面没有节点),为了统一做法需要用到哑元。
2. 例子
(1)以单链表节点删除问题为例:
模型:1 -> 2 -> 3 -> 4
删除节点2,3,4,只需要遍历时保存下待删除节点的前驱节点,然后修改前驱节点的next就好了,可是删除首元节点1时还需要判断是否是该节点是否是head指向的节点,如果是就还要移动head指针指向新的首元节点.
dummy -> 1 -> 2 -> 3 -> 4
而引入了哑元节点,对于1的删除也可以和后面的2,3,4的操作一样了,这样可以不用做边界条件判断
(2)以链表节点顺序交换问题为例:
链表节点顺序交换问题一般需要三个指针。
两两交换问题
见 【24.两两交换链表中的节点】
模型:pre->l1->l2->rest (l1,l2为待交换的两个结点)
很明显,交换头两个节点和交换其它相邻两个节点用到的方法是不同的,因而需要用到哑元。如果不用哑元而交换头两个节点又用相同方法,则由于待交换节点是从头两个节点开始的,而head之前又没有节点,那么pre就没有意义。
(3)链表反转问题
见 【206.反转链表】
只需要将操作节点插入到head之前即可,根本不需要操作head,因此不需要用到哑元。
(4)删除倒数第n个节点
见【19. 删除链表的倒数第 N 个结点】(https://www.cnblogs.com/trmbh12/p/17792635.html)