说明:本文内容来自于代码随想录
https://leetcode.cn/problems/design-linked-list/
https://leetcode.cn/problems/remove-linked-list-elements/description/,删除节点,虚拟头节点。定义两个节点,分别为前继节点 pre 和当前节点 cur。当前节点初始化为头节点。每次判断当前节点是否需要删除。若要删除,则将前继节点的下一个指向当前节点的下一个;否则,更新前继节点为当前节点。最后当前节点移动到下一个节点。
要点:
代码如下:
public ListNode removeElements(ListNode head, int val) {
// 前继节点的下一个指向当前节点
// 若当前节点需要删除,则将前继节点的下一个指向当前节点的下一个
ListNode dummy = new ListNode(-1, head); // 虚拟节点,指向头节点
ListNode pre = dummy;
ListNode cur = head;
while (cur != null) {
if (cur.val == val) { // 当前节点需要删除
pre.next = cur.next;
} else { // 当前节点不需要删除,则更新前继节点为当前节点
pre = cur;
}
cur = cur.next; // 当前节点往前移动一位
}
// 最开始,pre.next和dummy指向的实际上是同一个地址。当pre.next发生变化时,dummy.next也发生变化
// 但是pre和dummy不是同一个地址。所以当修改pre = cur时,dummy是不变的。
// 所以最开始如果pre.next发生了更新的话,那么dummy.next也会同步更新,即更新的是头节点。
// 一旦pre发生了更新,则下一次的pre.next更新就不会影响头节点了,影响的是头节点后面的节点。
return dummy.next;
}
public ListNode insertHead(ListNode head, int val) {
ListNode newNode = new ListNode(val);
newNode.next = head; // 新节点的后继指向旧头节点
head = newNode; // 更新头节点为新节点
return head;
}
思路:
动画:
https://code-thinking.cdn.bcebos.com/gifs/206.%E7%BF%BB%E8%BD%AC%E9%93%BE%E8%A1%A8.gif
迭代版
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
while (cur != null) {
// 保存cur的下一个节点
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
递归版
public ListNode reverse(ListNode pre, ListNode cur) {
if (cur == null) return pre;
// 反转
ListNode next = cur.next;
cur.next = pre;
return reverse(cur, next);
}
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
return reverse(pre, cur);
}
https://leetcode.cn/problems/swap-nodes-in-pairs/description/
交换涉及到 3 步,所以需要 3 个指针 pre, cur, next
,分别表示上一个的前继、上一个、下一个(注意图中的 cur 指的是这里的 pre,图里的 1 是这里的 cur,图里的 2 是这里的 next):
// 交换
cur.next = next.next;
next.next = cur;
pre.next = next;
注意需要更新头节点,即:当第一次交换完之后,更新头节点为 next
哑节点(dummy node)在链表中很常用,比如:
说明:由于这些操作有可能会修改头节点,所以在操作的时候,除了哑节点 dummy,还要定义 pre 节点: