虚拟头结点法

在头节点需要进行判断时,可以使用虚拟头节点,此时虚拟节点的next指向头结点。虚拟头结点在许多题目中都可以采用。

题目描述:

移除链表元素(力扣链接🔗

虚拟头结点 - 图1

题目分析:

删除节点的思路:

虚拟头结点 - 图2

此时头结点需要判断,所以右两种方法:

  • 直接使用原来的链表来进行删除操作。
  • 设置一个虚拟头结点在进行删除操作。

解法一:

在原来的链表删除。

虚拟头结点 - 图3

移除头结点和移除其他节点的操作是不一样的,因为链表的其他节点都是通过前一个节点来移除当前节点,而头结点没有前一个节点。

所以头结点如何移除呢,其实只要将头结点向后移动一位就可以,这样就从链表中移除了一个头结点。

虚拟头结点 - 图4

代码:

不添加虚拟节点方式

时间复杂度 O(n) , 空间复杂度 O(1)

  1. public ListNode removeElements(ListNode head, int val) {
  2. // 不使用虚拟的头节点的方法
  3. // 先将前面相等的移除,也就是移除头结点
  4. while (head != null && head.val == val) {
  5. head = head.next;
  6. }
  7. // 已经为空,提前退出
  8. if (head == null) {
  9. return head;
  10. }
  11. ListNode pre = head;
  12. ListNode cur = head.next;
  13. // 循环遍历移除
  14. while (cur != null) {
  15. if (cur.val == val) {
  16. // 删除此节点
  17. pre.next = cur.next;
  18. } else {
  19. pre = cur;
  20. }
  21. cur = cur.next;
  22. }
  23. return head;
  24. }

解法二:

设置虚拟头节点

虚拟头结点 - 图5

这里来给链表添加一个虚拟头结点为新的头结点,此时要移除这个旧头结点元素1。注意最后返回虚拟头节点的next。

代码:

  1. public ListNode removeElements(ListNode head, int val) {
  2. // 为空直接返回
  3. if (head == null) {
  4. return head;
  5. }
  6. ListNode dummy = new ListNode(-1, head); // 虚拟头结点
  7. ListNode pre = dummy;
  8. ListNode cur = dummy.next;
  9. while (cur != null) {
  10. if (cur.val == val) {
  11. pre.next = cur.next;
  12. } else {
  13. // 移动前指针到后指针的位置
  14. pre = cur;
  15. }
  16. cur = cur.next;
  17. }
  18. return dummy.next;
  19. }
  20. }

其他虚拟头结点案例

两两交换链表中的节点(🔗