想了很久,一道Microsoft的筆試題目 —— Reversing Linked List

onlyblues發表於2021-03-16

Reversing Linked List

Given a constant K and a singly linked list L, you are supposed to reverse the links of every K elements on L. For example, given L being 1→2→3→4→5→6, if K=3, then you must output 3→2→1→6→5→4; if K=4, you must output 4→3→2→1→5→6.

Input Specification:

Each input file contains one test case. For each case, the first line contains the address of the first node, a positive N (≤) which is the total number of nodes, and a positive K (≤) which is the length of the sublist to be reversed. The address of a node is a 5-digit nonnegative integer, and NULL is represented by -1.

Then N lines follow, each describes a node in the format:

Address Data Next

where Address is the position of the node, Data is an integer, and Next is the position of the next node.

Output Specification:

For each case, output the resulting ordered linked list. Each node occupies a line, and is printed in the same format as in the input.

Sample Input:

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

Sample Output:

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

 

我的思路

  姥姥(陳越)說這道題是微軟某年的筆試題目,還說不難。說實話,這道題我做了兩天,想了有8個多小時(是我太菜了)。主要是一直習慣了連結串列,結果來了個靜態連結串列,腦子一時轉不過來。前面一直在用暴力的解法,直到後面通過不斷的思考,才慢慢找到了思路。自己做出了,發現確實也不是很難。雖然花了很多時間,讓我很痛苦,不過還好自己沒有放棄這題,做了出來。只能說自己平時做得題太少了,腦子沒有動起來。

  回到這個題目。題目的大意是,第一行輸入靜態連結串列首個節點的地址,靜態連結串列的節點的個數N,每次連續反轉節點的個數K。然後下面N行輸入當前節點的地址Address,當前節點存放的資料Data,和它的下一個節點地址Next。然後會對連續的每K個節點進行反轉,最後輸出反轉後的結果。比如, 有1→2→3→4→5→6, 如果 K=3, 那麼應該輸出3→2→1→6→5→4。如果K=4, 那麼應該輸出4→3→2→1→5→6。

  這道題我看了其他人的解法,基本上都是申請100000大小的結構體陣列,然後把當前節點的地址作為陣列的下標,這樣子基本上就可以把靜態連結串列當作普通的連結串列來做了。

  主要是我一開始沒有想到這種做法,而且申請一個大小為100000的陣列,讓我感到很震驚。我覺得申請這麼大的空間太浪費了,雖然題目的記憶體限制為64 MB,所以沒有用這種方法,而是想出了別的解法。

  對於這一題,我寫了4份不同的程式碼。

  一開始我覺得難的地方在於很難找到下一個節點的陣列下標,所以直接通過遍歷整個陣列來找下一個節點的陣列下標,然後再根據K來改變next的值。還有,當時為了找到反轉後的最後一個節點應該指向的那個節點也花了不少時間,程式編得很複雜。如果有N個節點,那麼光是遍歷都要N2次。然後最後的輸出也是通過遍歷N2次。雖然最後這個程式通過了一些樣本點,但當N取最大值105時,很明顯是通過不了的。這個程式就不展示了。

  接下來是第2份,開始對這道題目有一點思路了。就是當N組的資料輸入完後,我再申請一個大小為N的結構體陣列,通過遍歷,使節點按照地址順序來存放。同時在這個過程中,通過STL裡面的reverse函式來對每K個節點進行反轉。最後再輸出。不過在按照順序來存放節點的過程中,我還是用遍歷N2次的方法來找下一個節點的陣列下標。這導致我還是執行超時,不過只有取最大N這一個樣本點沒通過,但解決了多餘節點不在連結串列上的問題。下面給出第2份的程式碼:

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 struct Node {
 5     int address;
 6     int data;
 7     int next;
 8 };
 9 
10 int main() {
11     int firstAddress, n, k;
12     scanf("%d %d %d", &firstAddress, &n, &k);
13     
14     Node node[n];
15     int pre;
16     for (int i = 0; i < n; i++) {
17         scanf("%d %d %d", &node[i].address, &node[i].data, &node[i].next);
18         if (node[i].address == firstAddress) pre = i;
19     }
20     
21     Node sqNode[n];     // 順序表,把每一個節點按順序儲存到這個陣列中,可以很方便找到下一個節點的陣列下標,同時也很方便反轉 
22     sqNode[0] = node[pre];
23     int cnt = 1;        // 用來記錄連續節點的個數,防止出現多餘的節點,遍歷輸出的時候用cnt 
24     
25     for (int i = 1; i < n && node[pre].next != -1; i++) {
26         for (int j = 0; j < n; j++) {
27             if (node[j].address == node[pre].next) {
28                 sqNode[i] = node[j];
29                 pre = j;
30                 cnt++;
31                 break;
32             }
33         }
34         
35         if (cnt % k == 0) std::reverse(sqNode + cnt - k, sqNode + cnt); // 每K個節點就反轉 
36     }
37     
38     for (int i = 0; i < cnt; i++) {
39         printf("%05d %d ", sqNode[i].address, sqNode[i].data);
40         if (i < cnt - 1) printf("%05d\n", sqNode[i + 1].address);
41         else printf("-1\n");
42     }
43     
44     return 0;
45 }

  這個程式碼部分正確,只是當N很大時,就會執行超時,提交結果如下(一開始的提交記錄丟了,所以又故意提交了第2份程式碼):

  然後我想了改進的演算法,就是在輸入N組資料的同時,就把輸入的當前節點的地址Address按從小到大的順序存放到一個陣列中,我選擇了插入排序。當把資料都輸入完後,N個節點就按它的地址Address從小到大排好在陣列中。然後再申請一個大小為N的陣列,這個時候就通過二分查詢來查詢下一個節點的地址從而找到下一個節點的陣列下標,再把這些節點按照按節點地址順序排好在另外一個陣列中,並且在排序的過程中通過reverse函式來對排好序的每K個節點進行反轉。這大大提高了效率,最後也通過了全部的樣本點!

  這是第3份程式的程式碼:

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 struct Node {
 5     int address;
 6     int data;
 7     int next;
 8 };
 9 
10 void insertSort(Node *node, int n);
11 int binarySearch(Node *node, int n, int keyAddress, int &cnt);
12 
13 int main() {
14     int firstAddress, n, k;
15     scanf("%d %d %d", &firstAddress, &n, &k);
16     
17     Node node[n];
18     for (int i = 0; i < n; i++) {
19         scanf("%d %d %d", &node[i].address, &node[i].data, &node[i].next);
20         insertSort(node, i);
21     }
22     
23     Node sqNode[n];
24     int pre, cnt = 0;
25     pre = binarySearch(node, n, firstAddress, cnt);
26     sqNode[0] = node[pre];
27     
28     for (int i = 1; i < n && node[pre].next != -1; i++) {
29         pre = binarySearch(node, n, node[pre].next, cnt);
30         sqNode[i] = node[pre];
31         
32         if (cnt % k == 0) std::reverse(sqNode + cnt - k, sqNode + cnt);
33     }
34     
35     for (int i = 0; i < cnt; i++) {
36         printf("%05d %d ", sqNode[i].address, sqNode[i].data);
37         if (i < cnt - 1) printf("%05d\n", sqNode[i + 1].address);
38         else printf("-1\n");
39     }
40     
41     return 0;
42 }
43 
44 void insertSort(Node *node, int n) {
45     Node t = node[n];
46     int i = n - 1;
47     for ( ; i >= 0 && t.address < node[i].address; i--) {
48         node[i + 1] = node[i];
49     }
50     node[i + 1] = t;
51 }
52 
53 int binarySearch(Node *node, int n, int keyAddress, int &cnt) {
54     int left = 0, right = n - 1, mid;
55     while (left <= right) {
56         mid = (left + right) / 2;
57         if (keyAddress < node[mid].address) {
58             right = mid - 1;
59         }
60         else if (keyAddress > node[mid].address) {
61             left = mid + 1;
62         }
63         else {
64             cnt++;
65             return mid;
66         }
67     }
68 }

  第4份和第3份的程式碼其實沒有什麼不同,只不過把插入排序換成了STL裡面的sort函式,一樣可以通過。

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 struct Node {
 5     int address;
 6     int data;
 7     int next;
 8 };
 9 
10 bool cmp(const Node &a, const Node &b);
11 int binarySearch(Node *node, int n, int keyAddress, int &cnt);
12 
13 int main() {
14     int firstAddress, n, k;
15     scanf("%d %d %d", &firstAddress, &n, &k);
16     
17     Node node[n];
18     for (int i = 0; i < n; i++) {
19         scanf("%d %d %d", &node[i].address, &node[i].data, &node[i].next);
20     }
21     
22     std::sort(node, node + n, cmp);    // 變化的地方,輸入完資料後再用sort函式來排序 
23     
24     Node sqNode[n];
25     int pre, cnt = 0;
26     pre = binarySearch(node, n, firstAddress, cnt);
27     sqNode[0] = node[pre];
28     
29     for (int i = 1; i < n && node[pre].next != -1; i++) {
30         pre = binarySearch(node, n, node[pre].next, cnt);
31         sqNode[i] = node[pre];
32         
33         if (cnt % k == 0) std::reverse(sqNode + cnt - k, sqNode + cnt);
34     }
35     
36     for (int i = 0; i < cnt; i++) {
37         printf("%05d %d ", sqNode[i].address, sqNode[i].data);
38         if (i < cnt - 1) printf("%05d\n", sqNode[i + 1].address);
39         else printf("-1\n");
40     }
41     
42     return 0;
43 }
44 
45 bool cmp(const Node &a, const Node &b) {
46     return a.address < b.address;
47 }
48 
49 int binarySearch(Node *node, int n, int keyAddress, int &cnt) {
50     int left = 0, right = n - 1, mid;
51     while (left <= right) {
52         mid = (left + right) / 2;
53         if (keyAddress < node[mid].address) {
54             right = mid - 1;
55         }
56         else if (keyAddress > node[mid].address) {
57             left = mid + 1;
58         }
59         else {
60             cnt++;
61             return mid;
62         }
63     }
64 }

 

一些感觸

  這道題目算是我到目前為止,所花時間最多的一道題目。不愧是大廠的筆試題目,我這種菜鳥花很長時間才勉強做了出來。也側面看出我平時見過的題目比較少。自己是非科班,平時有空就抽點時間來自學程式設計,學到現在還不到1年,估計了一下大概9個月左右。在這個過程中我體會到程式設計帶來的樂趣,也對各種高效的演算法感到十分敬佩。要走程式設計這條路肯定是要吃苦的,但程式設計是我的興趣,我只會一直走下去,學越來越多的計算機知識,永遠也不會放棄!還有,再過一兩個月我就參加計算機的轉專業考核了,希望這次能夠成功,圓我的程式設計之夢。

相關文章