1. 程式人生 > >1025 反轉連結串列 (25)(25 分)

1025 反轉連結串列 (25)(25 分)

1025 反轉連結串列 (25)(25 分)

給定一個常數K以及一個單鏈表L,請編寫程式將L中每K個結點反轉。例如:給定L為1→2→3→4→5→6,K為3,則輸出應該為3→2→1→6→5→4;如果K為4,則輸出應該為4→3→2→1→5→6,即最後不到K個元素不反轉。

輸入格式:

每個輸入包含1個測試用例。每個測試用例第1行給出第1個結點的地址、結點總個數正整數N(<= 10^5^)、以及正整數K(<=N),即要求反轉的子鏈結點的個數。結點的地址是5位非負整數,NULL地址用-1表示。

接下來有N行,每行格式為:

Address Data Next

其中Address是結點地址,Data是該結點儲存的整數資料,Next

是下一結點的地址。

輸出格式:

對每個測試用例,順序輸出反轉後的連結串列,其上每個結點佔一行,格式與輸入相同。

輸入樣例:

00100 6 4
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218

輸出樣例:

00000 4 33218
33218 3 12309
12309 2 00100
00100 1 99999
99999 5 68237
68237 6 -1

這道題可讓我波折 透了 最後一個情況無論怎麼調都報段錯誤。然後我重頭到腳檢查了一遍 沒有問題 非法資料 和不在連結串列上的節點100%是不會參與旋轉的。可是就是通過不了 徹底的抓狂了 ,我決定用 STL 重寫它 看一下到底是怎麼個一回事。

結果STL AC之後,我越想越不明白 使用 STL的 程式碼逐一替換掉 原來的程式碼 直到我看到了輸出函式。(就發現了造成段錯誤的非法語句~)

詳細思路 :

少量STL語句思路(就是交換的時候用了 STL )

0.建立結構體,三個參量 依次是 該節點的地址 該節點的資料 該節點的下一地址 ;定義兩個結構體陣列,一個成員變數陣列,另一個則是成員指標的陣列。因此用指標陣列主要是考慮到空間的問題,我們可以對指標陣列進行排序。這個比再賦一遍值要好得多。

1. 讀入資料 ,注意 讀入的資料應該存在一個 大小為 100000 的結構體數組裡面  下標不是從 0 開始 而是該節點的地址作為下標。 同時別忘記儲存該節點的 地址 ,因為該節點的地址不僅作為陣列的下標,更多的應該留心後面輸出的情況。[這是一種嚴謹的思想,而不是要等到用的時候再回來定義 最好要把情況都考慮到,這是對我的要求。]

2.讀入資料以後 就必須要“拉住頭”把連結串列給抽出來。這個時候 由於我們索引 連結串列所用的值是連結串列的當前節點的值 ,我們還知道連結串列結尾肯定是 -1 結束的 那麼 我們就非常輕而易舉的知道 最優的方法就是 來迴圈 (不是遞迴) 。迴圈出口就是 -1.

實施方案是 (T是 成員指標陣列的儲存資料大小,初始化為 0)

 int T=0;  for(int i = 頭結點索引值 ; i!=-1; i = 成員陣列[i].next)    成員指標陣列[T++] = &成員陣列[i];

3.頭被拉出來之後,根據題目要求 接下來是 "換" 怎麼換  答案是以 第一行第三個數為基準 只要它不大過.......(不大過什麼,這個很重要,可以想一下,是不大過 “第一行第二個數”嗎?? 是就危險了哦) 因為它有可能存在不是連結串列上的節點 ,所以應該是不大過 成員指標陣列的 儲存資料大小 T  。

4.轉換開始 轉換可以沿襲 之前 C 語言的 方法 給定範圍 然後 兩個變數 一個指向起點 另一個指向終點,兩個變數以撞車的方向前進移動,當兩個變數不相互碰撞的時候 交換 兩個儲存指標變數的 值。直到交換完成。這是內層迴圈

5.外層迴圈 主要控制範圍和 次數的 通常使用兩個變數i,j 一個令它等於 T 一個令它等於 0 ,執行迴圈的先決條件是 i>=翻轉值(這個值就是第一行第三個值) ,接著每次 迴圈結束以後 i-=翻轉值 ;那個j是用來控制交換起點的,一開始等於 0 迴圈結束 後 i+=翻轉值。 

6. 4 5 結束以後整個翻轉就全部完成了,接下來就是輸出的工作。

7.輸出採用預判斷的方法 先判斷該節點是不是等於 尾節點(所謂的尾節點 就是迴圈有沒有到 訪問 成員指標陣列[T-1]這個位置)

如果不是,就輸出 

printf("%05d %d %05d",成員指標陣列[當前遍歷變數]->ID,成員指標陣列[當前遍歷變數]->data,成員指標陣列[當前遍歷變數 + 1]->ID);

8.程式執行完畢。

下面是上面實現的程式碼:

#include <bits/stdc++.h>
using namespace std;
struct sn {
	int ID;
	int data;
	int next;
};
struct sn P[100000 + 10]; // 存輸入的內容
struct sn* Q[100000 + 10]; // 存輸入內容的指標
void solve() {
	int toulist, cs, XZ; // 6 個節點(cs)  4個大翻轉(XZ)
	scanf("%d%d%d", &toulist, &cs, &XZ);
	if (!cs)
		toulist = -1;
	for (int i = 0, inp, DE, NEXT; i < cs; i++) {
		scanf("%d%d%d", &inp, &DE, &NEXT);
		P[inp].data = DE;
		P[inp].next = NEXT;
		P[inp].ID = inp;
	}
	int tk = 0;
	for (int i = toulist; i != -1; i = P[i].next) {
		Q[tk++] = &P[i];
	}
	for (int i = tk,j=0;i>=XZ;i-=XZ,j+=XZ) {
		reverse(Q + j, Q + j + XZ);
	}
	for (int i = 0; i < tk; i++) {
		if (i != tk - 1) {  // 段錯誤是因為之前我這裡寫成了 i!=cs-1 導致越界訪問,炸
			printf("%05d %d %05d\n", Q[i]->ID, Q[i]->data, Q[i + 1]->ID);
		}
		else {
			printf("%05d %d -1", Q[i]->ID, Q[i]->data);
		}
	}
}
int main() {

	solve();
	system("pause");
	return 0;
}

值的一提的是 我還寫了另外的STL版本 那個版本時間效率不及 上面程式碼。但是就是因為這樣我才找出了段錯誤的原因 可以讓我暫時停止寫部落格 專心應付期末考試了 ~

STL實現: (思路相同 剛開始我是考慮可能陣列開小了才這麼寫的 不推薦使用下面的方法。雖然程式碼 AC了)

#include <bits/stdc++.h>
using namespace std;

map<int, pair<int, int> >SN;

struct GZ { // 結構體構造 函式 解決
	int NO_this;
	int data;
	int last;
};
struct GZ T[100000 + 100000 + 10];

void solve() {

	int toulist = -1, x, FZ;
	scanf("%d%d%d", &toulist, &x, &FZ);
	if (x == 0)
		toulist = -1;

	for (int i = 0, tNO_, P1, P2; i < x; i++) {
		scanf("%d", &tNO_), tNO_;
		scanf("%d", &P1), P1;
		scanf("%d", &P2), P2;
		SN[tNO_] = { P1 ,P2 };
	}
	int Tcnt = 0;
	for (int j = toulist; j != -1; j = SN[j].second)
		T[Tcnt++] = { j,SN[j].first,SN[j].second };

	for (int i = Tcnt, j = 0; i >= FZ; i -= FZ, j += FZ) {
		 reverse(T+j, T+j + FZ);
	}
	for (int i = 0; i < Tcnt; i++) {
		i == Tcnt - 1 ? printf("%05d %d -1\n", T[i].NO_this, T[i].data) : printf("%05d %d %05d\n", T[i].NO_this, T[i].data, T[i + 1].NO_this);
	}
}

int main() {
	solve();
	system("pause");
	return 0;
}