反转整个链表(简单)
很简单且经典的题目。就是将 1->2->3->4->5 反转为 5->4->3->2->1。
有两种思路,迭代和递归。先看图再看代码:
1)迭代——经典的双/三指针
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
while (cur != null) {
ListNode nxt = cur.next;
cur.next = pre;
pre = cur;
cur = nxt;
}
return pre;
}
}
2)递归——假设子问题已经解决
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
}
反转整个链表(进阶)
这里提供另一种思路,即一种不断“提到前面”的方法:
1)遍历到哪个节点,就将哪个节点提前到第一个;
2)这个过程需要借助dummy虚节点;
3)移动的双指针为cur和nxt(每次取cur的下一个为nxt;由于nxt不断提前,cur不断自动后移)
dummy -> 1 -> 2 -> 3 -> 4 -> 5 提前2
dummy -> 2 -> 1 -> 3 -> 4 -> 5 提前3
dummy -> 3 -> 2 -> 1 -> 4 -> 5 提前4
dummy -> 4 -> 3 -> 2 -> 1 -> 5 提前5
dummy -> 5 -> 4 -> 3 -> 2 -> 1 大功告成!
class Solution {
public ListNode reverseList3(ListNode head) {
if (head == null) {
return null;
}
ListNode dummy = new ListNode(-1, head);
ListNode pre = dummy;
ListNode cur = head;
ListNode nxt = null;
while (cur.next != null) {
nxt = cur.next;
cur.next = nxt.next;
nxt.next = pre.next;
pre.next = nxt;
}
return dummy.next;
}
}
反转部分链表(难点)
学会了迭代和递归就够了,为什么还要学习第三种“提前”法?正是为此题做准备:
如果使用迭代/递归,算法和实现和边界的处理都非常麻烦:
1)遍历一次,找到left和right的位置及其哨兵的位置;
2)截取链表,用迭代/递归进行反转,同时还要有头/尾的指针;
3)拼接回去;
而如果使用第三种方法,也就一次遍历的事儿:
1)for(left)找到pre的位置并固定;
2)for(right-left)对要反转的部分节点不断进行提前的操作;
3)结束;
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode pre = dummy;
ListNode cur = null;
ListNode nxt = null;
for (int i = 0; i < left - 1; i++) {
pre = pre.next;
}
cur = pre.next;
for (int i = 0; i < right - left; i++) {
// 这段取“nxt然后提前”的代码,像是在链表上穿针引线 >_<
nxt = cur.next;
cur.next = nxt.next;
nxt.next = pre.next;
pre.next = nxt;
}
return dummy.next;
}
}
E N D END END