Reverse a linked list from position m to n. Do it in one-pass.
Note: 1 ≤ m ≤ n ≤ length of list.
Example:
Input: 1->2->3->4->5->NULL, m = 2, n = 4 Output: 1->4->3->2->5->NULL
很奇怪為何沒有倒置鏈表之一,就來了這個倒置鏈表之二,不過猜也能猜得到之一就是單純的倒置整個鏈表,而這道作為延伸的地方就是倒置其中的某一小段。對於鏈表的問題,根據以往的經驗一般都是要建一個dummy node,連上原鏈表的頭結點,這樣的話就算頭結點變動了,我們還可以通過dummy->next來獲得新鏈表的頭結點。這道題的要求是只通過一次遍歷完成,就拿題目中的例子來說,變換的是2,3,4這三個點,我們需要找到第一個開始變換結點的前一個結點,只要讓pre向后走m-1步即可,為啥要減1呢,因為題目中是從1開始計數的,這里只走了1步,就是結點1,用pre指向它。萬一是結點1開始變換的怎么辦,這就是我們為啥要用dummy結點了,pre也可以指向dummy結點。然后就要開始交換了,由於一次只能交換兩個結點,所以我們按如下的交換順序:
1 -> 2 -> 3 -> 4 -> 5 -> NULL
1 -> 3 -> 2 -> 4 -> 5 -> NULL
1 -> 4 -> 3 -> 2 -> 5 -> NULL
我們可以看出來,總共需要n-m步即可,第一步是將結點3放到結點1的后面,第二步將結點4放到結點1的后面。這是很有規律的操作,那么我們就說一個就行了,比如剛開始,pre指向結點1,cur指向結點2,然后我們建立一個臨時的結點t,指向結點3(注意我們用臨時變量保存某個結點就是為了首先斷開該結點和前面結點之間的聯系,這可以當作一個規律記下來),然后我們斷開結點2和結點3,將結點2的next連到結點4上,也就是 cur->next = t->next,再把結點3連到結點1的后面結點(即結點2)的前面,即 t->next = pre->next,最后再將原來的結點1和結點2的連接斷開,將結點1連到結點3,即 pre->next = t。這樣我們就完成了將結點3取出,加入結點1的后方。第二步將結點4取出,加入結點1的后方,也是同樣的操作,這里就不多說了,請大家自己嘗試下吧,參見代碼如下:
class Solution { public: ListNode *reverseBetween(ListNode *head, int m, int n) { ListNode *dummy = new ListNode(-1), *pre = dummy; dummy->next = head; for (int i = 0; i < m - 1; ++i) pre = pre->next; ListNode *cur = pre->next; for (int i = m; i < n; ++i) { ListNode *t = cur->next; cur->next = t->next; t->next = pre->next; pre->next = t; } return dummy->next; } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/92
類似題目:
參考資料:
https://leetcode.com/problems/reverse-linked-list-ii/
https://leetcode.com/problems/reverse-linked-list-ii/discuss/30668/12-lines-4ms-C%2B%2B