leetcode#92 Reverse Linked List II

Reverse a linked list from position m to n. Do it in-place and in one-pass.

For example:
Given 1->2->3->4->5->NULL, m = 2 and n = 4,

return 1->4->3->2->5->NULL.

Note:
Given m, n satisfy the following condition:
1 ≤ mn ≤ length of list.

解释:

给定一条单链表,要求反转第m个节点到第n个节点之间链表节点顺序。

注意,限定输入的关系为:1 <= m <= length of list

理解:

之前已经写过单链表的反转,本题可以理解为从节点m到节点n,这一段的单链表反转,即可以继续使用之前的单链表反转代码对这一段的链表进行操作。但是,由于这不是链表所有节点都参与反转,所以还需要根据实际情况分别考虑:

  • m = 1 ,这时候在改变中间链表节点的顺序之后,原链表的头结点指针head需要指向节点n的位置,而原来的头结点将会成为原来节点n的下一个节点的前向节点;
  • m != 1 ,这时候情况就相对简单一点,在改变中间一段链表的顺序之后,只需要将原m节点的指针指向原n节点的下一个节点,再将原m节点的前向节点的指针指向原n节点,即可。

根据上述理解,在原有链表反转代码的基础上,需要增加条件判断,以及指向节点m前向节点的pfix指针和用于遍历链表节点的pmove指针和next指针,以及用于计数并与(n - m)进行比较的计数变量count

我的解法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
public class Solution {
public ListNode reverseBetween(ListNode head, int m, int n) {
if(head == null || head.next == null || (n - m) == 0) { return head; }
ListNode pfix = head;
for(int i = 1;i < (m - 1);i++) {
pfix = pfix.next;
}
ListNode pmove = pfix.next;
ListNode tail = null;
int count = 0;
while(pmove != null && count < (n - m)) {
ListNode next = pmove.next;
pmove.next = tail;
tail = pmove;
pmove = next;
count++;
}
if(m == 1) {
head = tail;
pfix.next.next = pfix;
pfix.next = pmove;
}
else{
pfix.next.next = pmove.next;
pfix.next = pmove;
pmove.next = tail;
}
return head;
}
}
大神解法:

很简洁的方法!

巧妙之处在于,该解法在链表之外创建了一个新的节点dummy,指向链表的头结点,并令指针pre为始终指向节点m的上一个节点,如此一来就可以避免判断m == 1以及相应的不同情况的不同处理方式了。

同时,本解法中对于链表节点的位置调换方式也很特别,举个例子:

对于0->1->2->3->4->5->null && m=1,n=4来说,调换的结果依次为:

1->0 ->2->3->4->5->null

2->1->0 ->3->4->5->null

3->2->1->0 ->4->5->null

可见,下一次的顺序调换,都会基于上一次调换的结果,将上一次顺序调换的结果作为一个整体,与下一个节点进行调换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public ListNode reverseBetween(ListNode head, int m, int n) {
if(head == null) return null;
ListNode dummy = new ListNode(0); // create a dummy node to mark the head of this list
dummy.next = head;
ListNode pre = dummy; // make a pointer pre as a marker for the node before reversing
for(int i = 0; i<m-1; i++) pre = pre.next;
ListNode start = pre.next; // a pointer to the beginning of a sub-list that will be reversed
ListNode then = start.next; // a pointer to a node that will be reversed
// 1 - 2 -3 - 4 - 5 ; m=2; n =4 ---> pre = 1, start = 2, then = 3
// dummy-> 1 -> 2 -> 3 -> 4 -> 5
for(int i=0; i<n-m; i++)
{
start.next = then.next;
then.next = pre.next;
pre.next = then;
then = start.next;
}
// first reversing : dummy->1 - 3 - 2 - 4 - 5; pre = 1, start = 2, then = 4
// second reversing: dummy->1 - 4 - 3 - 2 - 5; pre = 1, start = 2, then = 5 (finish)
return dummy.next;
}

总结:

  • 当链表的头结点不好处理的时候,可以在链表之外创建一个新的节点,指向链表的头结点;
  • 每次操作都基于前一次结果的方法,学习学习;
  • 熟练掌握基础方法,是解决复杂问题的根本。