前言
一. 反转链表
原题链接
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
思路如下: 我们可以通过一次遍历,修改链表节点的指针指向。由于指针修改的时候,指针的引用就变了。我们需要用一个临时变量tmp
来保存节点。同时我们需要一个变量pre
来标识上一个节点。以及cur
代表当前节点。
那么我们以上图为例,原本的指针是 1 --> 2
。我们要做的就是断开这个链接。然后将指针改为 2 --> 1
。别看这个操作看似简单,但是真正的实现需要分为如下几个步骤:
- 首先,
pre
的值为null
,cur
的值就是链表头结点1。我们要提前存储2这个节点的引用(后续指针改变了,就拿不到了),也就是tmp = cur.next;
- 然后,将当前指针的
next
,改为上一个节点。即反转的实现:cur.next=pre;
这里其实就是相邻节点的一个指针反转了。但是我们既然要整个链表都反转,我们就难以避免递归。因此我们还需要将cur
和pre
的指针往后移动。不断地循环上述操作。即:
pre
指针后移一位:pre = cur;
cur
指针后移一位:cur = tmp
(tmp
是最开始存储的cur.next
引用)如图:
循环终止条件:cur
当前指针不为null
。最终完整代码如下:
public ListNode reverseList(ListNode head) {
ListNode cur = head, pre = null, tmp = null;
while (cur != null) {
// 1.临时存储下一个节点的引用
tmp = cur.next;
// 2.指针反转的核心步骤
cur.next = pre;
// 3.pre(上一个指针)往后移
pre = cur;
// 4.cur指针同样往后移,进入下一次循环进行指针反转
cur = tmp;
}
return pre;
}
二. 反转链表II
思路如下:
- 首先找到反转链表的前置节点
pre
(不在反转范围内),同时拿到反转链表部分的第一个节点:leftNode
。 - 在找到反转链表部分的最后一个节点
rightNode
。这样也可以拿到反转链表的后置节点last
。 - 将反转链表的前后指针断开,再对这部分链表做反转,代码同第一题,入参为
leftNode
。 - 反转结束,将左右两侧指针接回。
pre.next = rightNode
。对于leftNode
(此时是反转后的最后一个节点)leftNode.next = last
。
即链表表示:xxx->xxx->pre--> leftNode-->xxx-->rightNode -->last-->xxx
,[leftNode,rightNode]
这部分则是做反转操作。
代码如下:
public void reverseList2(ListNode head) {
ListNode cur = head, pre = null, tmp;
while (cur != null) {
tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
}
public ListNode reverseBetween(ListNode head, int left, int right) {
// 记录一个虚拟指针
ListNode dummy = new ListNode(-1);
dummy.next = head;
// 找到切割链表的前置节点pre
ListNode pre = dummy;
for (int i = 0; i < left - 1; i++) {
pre = pre.next;
}
// 找到反转链表的最后一个节点last(属于反转链表内的元素)
ListNode rightNode = pre;
for (int i = 0; i < right - left + 1; i++) {
rightNode = rightNode.next;
}
// 要想对 [pre.next , last] 区间的链表做反转,先要把两侧的指针给断掉
ListNode leftNode = pre.next, last = rightNode.next;
// 切断连接
pre.next = null;
rightNode.next = null;
reverseList2(leftNode);
// 连接
pre.next = rightNode;
leftNode.next = last;
return dummy.next;
}