在对this answer的评论中提出了一个想法,即反转一个简单的链表只能在O(nlog(n))中完成,而不能在O(n)时间内完成。
这绝对是错误的 - O(n)反转不是问题 - 只需遍历列表并随时更改指针。需要三个临时指针 - 这是恒定的额外内存。
我完全理解O(nlog(n))比O(n)更差(更慢)。
但出于好奇 - 可能是一个用于反转简单链接列表的O(nlog(n))算法?具有恒定额外内存的算法是优选的。
答案 0 :(得分:11)
我觉得你很困惑。你说O(n log(n))实际上比O(n)差。你或许是指O(log n)?如果是这样,答案是否定的。您无法在O(log n)中反转链接列表。 O(n)是微不足道的(显而易见的解决方案)。 O(n log(n))没有多大意义。
编辑:好的,所以你的意思是O(n log(n))。然后答案是肯定的。怎么样?简单。您对列表进行排序:
总费用:O(n log(n))
尽管有所有中间步骤,但排序是最昂贵的操作。 O(n)个其他步骤是常数(意味着步数不是n的因子),因此总成本为O(n log(n))。
编辑2:我最初没有按随机顺序放置列表项,但意识到你可以说已经排序的列表上的有效排序小于O(n log(n)即使你正在逆转它。现在我并不完全相信这种情况,但上述修订版本消除了这种潜在的批评。
是的,这是一个病态问题(和答案)。当然你可以在O(n)中完成。
答案 1 :(得分:7)
每个O(n)算法也是O(n log n),所以答案是肯定的。
答案 2 :(得分:1)
嗯...
你可以使用一个接受链表并通过链接列表的两半调用自身来反转它的递归,如果输入只是两个节点,它会反转它们。
这非常低效,但我相信它是O(nlog(n))...
伪代码中的以下内容(假设您有一个len
函数返回O(n)中列表的长度,以及一个sub_list(list, start_id, end_id)
函数返回从start_id开始的列表子集并以O(N)中的end_id结束:
function invert(list)
if len(list) == 1 return list
if len(list) == 2:
new_list = list.next
new_list.next = list
return new_list
middle_of_list = len(list) / 2 <-- integer division
first_half = invert ( sub_list(list, 1, middle_of_list) )
second_half = invert ( sub_list(list, middle_of_list+2, len(list) )
first_half.next = second_half
return first_half
答案 3 :(得分:1)
愚蠢的想法,但是O(n log n)而不是O(n)
答案 4 :(得分:0)
如果你是迂腐的话,那么这个算法就是O(n log n),因为指针的大小至少是log n,必须分配n次。
但实际上机器有一个固定的字大小,所以通常不会考虑这一点。
答案 5 :(得分:0)
如果问题实际上是Ω(nlgn)算法,那么这个过于复杂的算法可能会这样做吗?