我们有一个大小为L的链表,我们想要检索最后一个元素的第n个。
解决方案1:天真的解决方案
解决方案2:使用2个指针p1,p2
n
个元素时,p2也开始迭代两种解决方案似乎都具有相同的时间复杂度(即,2L - n
迭代超过列表元素)
哪一个更好?
答案 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只是采用最懒惰的方法,不要过度思考它。