获取链表中最后一个元素的第n个

时间:2013-09-16 00:50:08

标签: algorithm linked-list

我们有一个大小为L的链表,我们想要检索最后一个元素的第n个。

解决方案1:天真的解决方案

  • 从头到尾进行第一次传递以计算L
  • 从头到尾进行第二次传递

解决方案2:使用2个指针p1,p2

  • p1从头开始迭代,p2不移动。
  • 当p1和p2之间有n个元素时,p2也开始迭代
  • 当p1到达列表末尾时,p2处于预期位置

两种解决方案似乎都具有相同的时间复杂度(即,2L - n迭代超过列表元素) 哪一个更好?

4 个答案:

答案 0 :(得分:2)

这两种算法都是两遍的。对于相当小的n,第二个可以具有更好的性能,因为第二遍访问已经被第一遍传递的存储器。 (传递是交错的。)

一次通过解决方案会将指针存储在循环缓冲区或队列中,并在到达列表末尾后返回队列的“头部”。

答案 1 :(得分:2)

如何使用3个指针p,q,r和一个计数器。

使用p更新计数器迭代列表。 每n个节点将r分配给q,q分配给p

当你点击列表的末尾时,你可以弄清楚到底有多远 r来自列表的末尾。

你可以在不超过O(L + n)

的情况下得到答案

答案 2 :(得分:1)

如果n << L,解决方案2通常更快,因为缓存,即包含p1和p2的内存块被复制到CPU缓存一次并且指针在需要访问RAM之前移动一堆迭代试。

答案 3 :(得分:0)

简单地将链表的长度存储在O(1)内存中会不会便宜得多?你必须做“第一次通过”的唯一原因是因为你不知道链表的长度。如果存储长度,则可以每次迭代(| L | -n)元素并轻松检索元素。对于与L相比较高的n值,这种方式可以节省大量时间。例如,如果n等于| L |,则可以简单地返回列表的头部而不进行迭代。

此方法使用比第一个算法稍多的内存,因为它将长度存储在内存中,但是第二个算法使用两个指针,而此方法仅使用1个指针。如果你有第二个指针的内存,你可能有内存来存储链表的长度。

在纯理论中,授予O(| L | -n)相当于O(n),但是存在“快速”线性算法,然后存在“慢”线性算法。针对此类问题的两遍算法很慢。

正如@HotLicks在评论中指出的那样,“人们需要明白,”大O“复杂性在很多情况下只与实际表现松散相关,因为它忽略了附加因子和常数乘数。”在这种情况下,IMO只是采用最懒惰的方法,不要过度思考它。