[LeetCode 92] Reverse Linked List II 翻转单链表II
对于链表的问题,根据以往的经验一般都是要建一个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的后方,也是同样的操作,这里就不多说了,请大家自己尝试下吧,参见代码如下:
1 class Solution { 2 public: 3 ListNode* reverseBetween(ListNode* head, int m, int n) { 4 if(m==n) return head; 5 ListNode* beginPtr;//指向待翻转子链前一个节点 6 ListNode* w;//工作指针 7 if(m==1) 8 { 9 beginPtr = nullptr; 10 w = head; 11 } 12 else 13 { 14 beginPtr = head; 15 int i = m-1; 16 while(--i) 17 { 18 beginPtr = beginPtr->next; 19 } 20 w = beginPtr->next; 21 } 22 ListNode* subHead = w;//翻转前第m个节点 23 ListNode* pre = nullptr; 24 ListNode* post = nullptr; 25 int subsize = n-m+1;//待翻转子链表长度 26 while(subsize--) 27 { 28 post = w->next; 29 w->next = pre; 30 pre = w; 31 w = post; 32 if(beginPtr) 33 { 34 beginPtr->next = pre; 35 } 36 } 37 subHead->next = w; 38 39 if(m==1) return pre; 40 else return head; 41 } 42 };
优化后代码:
1 class Solution { 2 public: 3 ListNode *reverseBetween(ListNode *head, int m, int n) { 4 ListNode *dummy = new ListNode(-1), *pre = dummy; 5 dummy->next = head; 6 for (int i = 0; i < m - 1; ++i) pre = pre->next; 7 ListNode *cur = pre->next; 8 for (int i = m; i < n; ++i) { 9 ListNode *t = cur->next; 10 cur->next = t->next; 11 t->next = pre->next; 12 pre->next = t; 13 } 14 return dummy->next; 15 } 16 };