想了好久,一道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个月左右。在这个过程当中我体会到编程带来的乐趣,也对各类高效的算法感到十分敬佩。要走编程这条路确定是要吃苦的,但编程是个人兴趣,我只会一直走下去,学愈来愈多的计算机知识,永远也不会放弃!还有,再过一两个月我就参加计算机的转专业考核了,但愿此次可以成功,圆个人编程之梦。

相关文章
相关标签/搜索