【LeetCode】25. Reverse Nodes in k-Group 鏈表中k個元素倒置

一、概述

24題兩兩調換元素他爹。這個我沒想好怎麼用奇偶鏈表做,用二階指針或者遞歸也感覺太麻煩。因此選擇用棧做。

做出來了,但是效果很辣雞。

二、分析

1、我的方法

循環,棧中壓入k個元素,再讀取k個元素即可。代碼如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
	ListNode* reverseKGroup(ListNode* head, int k) {
        stack<ListNode*> s;
        ListNode *result = new ListNode(-1);
        ListNode *now, *tail;
        tail = head;
        now = result;
        while (tail != NULL)
		{
			int i;
            ListNode *tmp=tail;
			for (i = 0; i<k; i++)
			{
				s.push(tmp);
				tmp = tmp->next;
				if (tmp == NULL&&i!=k-1)
					break;
			}
			if(tmp==NULL&&i!=k)
                break;
            tail = tmp;
			while (s.size() != 0)
			{
				now->next = s.top();
				s.pop();
				now = now->next;
			}
		}
        now->next=tail;
        return result->next;
	}
};

有以下幾點要注意:

第一,調換k個元素,最後要是不夠k個元素則不調換。那問題來了,我又不知道什麼時候到最後,發現不夠k個的時候元素已經壓入棧了,怎麼辦?要用一個tmp指向棧底元素,當發現不夠直接退出壓入循環,指針重新指向tmp指向的元素即可。

第二,壓棧循環的退出條件,一個是到k個,另外一個是到末尾。到末尾又有兩種情況,一種是到已經滿足k個,一種是還沒滿足。由於要在循環體結束後進行判斷,因此存在到末尾且有k個元素壓入棧的時候,提前退出。這樣一來,我們即使到末尾,到底是出棧還是指向棧底元素就需要兩個判斷了:一個是沒到k個元素,一個是末尾爲NULL。缺一不可。

然後是出棧,在結尾加元素。

當所有的k個元素的序列都調換完,最後剩的元素也要加上,無論是剩下幾個還是剩下NULL。否者會出現循環鏈表導致TLE。

別的就沒有什麼注意的了。

2、指針迭代法

我就知道有這種方法,可惜那天沒耐心,沒仔細往下做。

先來看一張圖吧。用圖來講更直觀容易。

我們要維護五個指針:a、b、c、pre、tmp。

在24題中,我們已經知道,調換完所有元素之後,有一個指針的指向錯誤要在下一次循環中修正,在本題中體現爲“上一次循環的第一個”指向“本次循環的最後一個”。這裏的第一個和最後一個都指的是沒調換順序時候的鏈表順序。

因此我們用pre來保存前者,tmp來保存後者。a、b、c則負責調換工作。a指向本組k個元素的第一個,b是第二個,c是第三個。

進行調換前是上圖中的第一個鏈表,調換一次後則是第二個鏈表。

調換代碼如下:

b->next = a;
a = b;
b = c;
c = c->next;

在調換完k-1次之後進行下一步操作。如下圖。

我們已經完成了k個元素順序的調換,接下來要開始下一組的k個元素了。在開始之前,要先對上一個循環中的指向錯誤進行修正。即令pre->next指向a。然後令tmp成爲新的pre,b成爲新的tmp。開始下一個循環。

因爲元素總數很可能不能被k整除,因此在調換到最後的時候,若不夠k個就不動它們。但是我們是一組一組調換的,調換到一半發現不夠k個就會傻眼。因此要提前知道鏈表中元素的總數,然後再開始調換。這樣十分直觀易懂。雖然需要多遍歷鏈表一次。但這點時間的耗費是值得的。

循環退出條件就可以是num<k了。在退出後把最後的接上就可以。

代碼如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
	ListNode* reverseKGroup(ListNode* head, int k) {
        if(k==1)
            return head;
		ListNode *a, *b, *c, *tmp, *pre;
		int num = 0;
		tmp = head;
		while (tmp != NULL)
		{
			tmp = tmp->next;
			num++;
		}
        if(num<k)
            return head;
		ListNode* result = new ListNode(-1);
		result->next = head;
		tmp = head;
		pre = result;
		while (num>=k)
		{
			a = tmp;
			b = a->next;
			c = b->next;
			int n;
			for (n = 0; n<k - 1; n++,num--)
			{
				b->next = a;
				a = b;
				b = c;
                if(c==NULL)
                    break;
				c = c->next;
			}
			num--;
			pre->next = a;
			pre = tmp;
			tmp = b;
		}
		pre->next = b;
		return result->next;
	}
};

三、總結

指針的運用啊,最好還是畫圖解決,要不指針一多就暈暈的。但是效率真的高。

另外,有時候多遍歷一次鏈表的代價是值得的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章