想了很久,一道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.
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个月左右。在这个过程中我体会到编程带来的乐趣,也对各种高效的算法感到十分敬佩。要走编程这条路肯定是要吃苦的,但编程是我的兴趣,我只会一直走下去,学越来越多的计算机知识,永远也不会放弃!还有,再过一两个月我就参加计算机的转专业考核了,希望这次能够成功,圆我的编程之梦。
想了很久,一道Microsoft的笔试题目 —— Reversing Linked List的更多相关文章
- I am Nexus Master!(虽然只是个模拟题。。。但仍想了很久!)
I am Nexus Master! The 13th Zhejiang University Programming Contest 参见:http://www.bnuoj.com/bnuoj/p ...
- 【2020Java最新学习路线】写了很久,这是一份最适合普通大众、科班、非科班的路线
点赞再看,养成习惯,微信搜索[三太子敖丙]关注这个互联网苟且偷生的工具人. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的 ...
- 很久之前写的Ajax库
很久之前写的一个小型AJAX的js,放在上面以免以后想玩了找不到了. // version : 0.1 beta // author : __Ajax function __Ajax(url,opti ...
- 用struts2标签如何从数据库获取数据并在查询页面显示。最近做一个小项目,需要用到struts2标签从数据库查询数据,并且用迭代器iterator标签在查询页面显示,可是一开始,怎么也获取不到数据,想了许久,最后发现,是自己少定义了一个变量,也就是var变量。
最近做一个小项目,需要用到struts2标签从数据库查询数据,并且用迭代器iterator标签在查询页面显示,可是一开始,怎么也获取不到数据,想了许久,最后发现,是自己少定义了一个变量,也就是var变 ...
- SVN模型仓库中的资源从一个地方移动到另一个地方的办法(很久才解决)
弄了很久,想使用domove这个操作,但是都失败了.最后给svnkit的邮箱写了封邮件,他们告诉我这样做就成功了.实际上是使用docopy这个函数实现了move操作.package com.repos ...
- mysql 去除重复 Select中DISTINCT关键字的用法 在使用mysql时,有时需要查询出某个字段不重复的记录,虽然mysql提供 有distinct这个关键字来过滤掉多余的重复记录只保留一条,但往往只用它来返回不重复记录的条数,而不是用它来返回不重记录的所有值。其原因是 distinct只能返回它的目标字段,而无法返回其它字段,这个问题让我困扰了很久,用distinct不能解决的话,
在使用mysql时,有时需要查询出某个字段不重复的记录,虽然mysql提供 有distinct这个关键字来过滤掉多余的重复记录只保留一条,但往往只用它来返回不重复记录的条数,而不是用它来返回不重记 ...
- 使用Fragment应用放置后台很久,被系统回收,出现crash
使用Fragment应用放置后台很久,被系统回收,出现crash:原因:系统做了源码FragmentActivity调用onSaveInstanceState保存Fragment对象,这时候系统恢复保 ...
- Yii2 关于yii\db\Connection::open耗时很久的问题(1000ms左右)
关于这个问题我纠结了很久,每次打开网页yii\db\Connection::open几乎都耗时1000ms. 其实这个问题很好解决:只要把config\db.php配置信息里的localhost,改成 ...
- chrome浏览器上传图片反应很慢,延迟很久才弹出窗口
chrome浏览器上传图片反应很慢,延迟很久才弹出窗口 一个上传图片的控件,点击按钮上传图片,用chrome(谷歌浏览器)时,点击按钮后,要等好几秒才弹出文件选择窗口 可以试一下,把网络断开,这个问题 ...
随机推荐
- input composition event All In One
input composition event All In One input event compositionStart & compositionEnd & compositi ...
- Swift private(set) All In One
Swift private(set) All In One SwiftUI Getters and Setters https://docs.swift.org/swift-book/Language ...
- prefetch & preload & prerender & dns-prefetch & preconnect
prefetch & preload & prerender & dns-prefetch & preconnect performance optimization ...
- Iterators & Generators in depth
Iterators & Generators in depth https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/It ...
- js useful skills blogs
js useful skills blogs blogs https://davidwalsh.name/tutorials/javascript https://www.ruanyifeng.com ...
- 如何使用 js 实现一个 Promise.all 方法 PromiseAll
如何使用 js 实现一个 Promise.all 方法 PromiseAll Promise.all PromiseAll https://developer.mozilla.org/en-US/do ...
- HOC in Depth
HOC in Depth Higher-Order Components https://reactjs.org/docs/higher-order-components.html 1. wrappe ...
- Flutter 学习路径
Flutter 学习路径 docs https://flutter.dev/docs https://flutter.dev/community/china https://flutter-io.cn ...
- 高倍币VAST了解一下,如何掀起算力挖矿新热潮?
随着比特币.以太坊等主流数字货币的起起落落,市场对于数字货币交易似乎进入了冷却期.很多生态建设者开启了观望态度,机构以及巨鲸们也开始纷纷着手分散投资.就在此时,一个新的概念逐步露出头角,吸引了大众关注 ...
- 几个小实践带你快速上手MindSpore
摘要:本文将带大家通过几个小实践快速上手MindSpore,其中包括MindSpore端边云统一格式及华为智慧终端背后的黑科技. MindSpore介绍 MindSpore是一种适用于端边云场景的新型 ...