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

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.html

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.node

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

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 33218spa

 

個人思路

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

  回到這個題目。題目的大意是,第一行輸入靜態鏈表首個節點的地址,靜態鏈表的節點的個數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。code

  這道題我看了其餘人的解法,基本上都是申請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個月左右。在這個過程當中我體會到編程帶來的樂趣,也對各類高效的算法感到十分敬佩。要走編程這條路確定是要吃苦的,但編程是個人興趣,我只會一直走下去,學愈來愈多的計算機知識,永遠也不會放棄!還有,再過一兩個月我就參加計算機的轉專業考覈了,但願此次可以成功,圓個人編程之夢。

相關文章
相關標籤/搜索