最近想找些笔试题练练,于是就看到了微软05年的一道面试题:
给定一单链表的表头指针和指向其中一个节点的指针,要求以该指针为头将原链表逆序排列,例如:
N1->N2->N3->N4->N5->NULL pHEAD = N1,pSTART = N3,返回N3->N2->N1->N5->N4->NULL
N1->N2->N3->N4->N5->NULL pHEAD = N1,pSTART = N5,返回这个N5->N4->N3->N2->N1->NULL
N1->N2->N3->N4->N5->NULL pHEAD = N1,pSTART = N1,返回这个N1->N5->N4->N3->N2->NULL
不允许额外分配存储空间,不允许递归,可以使用临时变量。
不多说了,先贴出代码:
尽管题目不要求使用递归,但我还是想试一下,因为这个程序用递归的话也不是太好写,反正锻炼一下嘛。不过既然已经写了,就想比较一下递归和非递归算法的优劣。
递归和非递归算法的思想其实都很简单:
(1)在递归算法中,首先从start节点开始向后遍历,直到prev->next == &(*start)为止。这时递归程序层层出栈,各个节点就按逆序压入新的链表sl2中。当然在链尾处需要特别处理一下。
(2)在非递归算法中,还是从start开始向后遍历,但这时是把指向各节点的指针存放到一个数组里,然后将数组中的各个指针逆序提取出来,压入sl2。在链尾处也需要特别处理一下。
说的简单,但还是写了一大篇,呵呵。
还有,题目要求不能额外分配存储空间,但我觉得还是把原来链表保存下来比较好。因为这样可以对所有的情况进行测试。否则一遍测试之后,原链表找不到了,其他情况怎么处理呢?所有我就新增加了一个链表sl2,当然有点不符合题意了。O(∩_∩)O~
测试结果如下
递归情况下:
节点数 1e6 2e6 4e6 8e6
耗时 0.281 0.531 1.078 2.125
内存 -- -- -- 400多M
非递归情况下:
节点数 1e6 2e6 4e6 8e6
耗时 0.219 0.406 0.844 1.657
内存 -- -- -- 200多M
在数据较少的时候(比如在几百个节点时),两者几乎没什么区别,但是对于大规模数据,递归的花销,尤其是内存,实在是太大了。
ps.
在处理大规模数据时,VS可能会报出堆栈溢出的异常。这时就要设置一下编译器的堆栈大小:在VS2008中,找到
项目->属性->连接器->系统,修改"堆栈保留大小"或"堆栈提交大小"的任意一项即可。