【问题标题】:Why is mergesort space complexity O(log(n)) with linked lists?为什么链表的归并排序空间复杂度为 O(log(n))?
【发布时间】:2014-08-02 00:02:22
【问题描述】:

数组上的合并排序的空间复杂度为 O(n),而链表上的合并排序的空间复杂度为 O(log(n)),记录在 here

我相信我理解数组的情况,因为我们在合并两个子数组时需要辅助存储。但是链表合并排序不会只是合并两个子链表吗?我认为这将具有 O(1) 的空间复杂度来创建一个新的头部。

就地合并(无辅助存储):

public Node merge(Node a, Node b) {
    Node dummyHead, curr; dummyHead = new Node(); curr = dummyHead;
    while(a !=null && b!= null) {
        if(a.info <= b.info) { curr.next = a; a = a.next; }
        else { curr.next = b; b = b.next; }
        curr = curr.next;
    }
    curr.next = (a == null) ? b : a;
    return dummyHead.next;
}

解释一下就好了。

【问题讨论】:

  • O(n) ?这一定是新事物。我知道最好的平均排序复杂度是 O(nlogn)。
  • @thecoder 问题是关于空间复杂度,而不是时间复杂度。
  • 哦,那我道歉。我的错。
  • 请注意,这是专门关于 递归 合并排序的。您可以编写空间复杂度为 O(1) 的迭代合并排序。

标签: arrays algorithm sorting linked-list


【解决方案1】:

合并排序是一种递归算法。每个递归步骤都会在堆栈上放置另一个帧。对 64 项进行排序将比 32 项多采用一个递归步骤,实际上,当空间需求为 O(log(n)) 时,所指的是堆栈的大小。

【讨论】:

  • 啊,是的,有道理!
  • 另外,您可以使用迭代版本,它只需要恒定数量的整数和指针,但您需要O(log n) 位来表示整数或指针。
  • @tmyklebu:你混淆了两个不同的东西。您可以选择将 0..n-1 范围内的 1 个整数建模为占用 O(1) 空间(通常的方法),或者占用 O(log n) 空间;如果您选择后者(您似乎正在这样做),则递归合并排序需要 O(log^2(n)) 空间(O(log n) 堆栈级别,每个堆栈级别至少需要 2 个 O(log n )位每个)。我怀疑链表的 O(n log n) 空间复杂度是因为迭代版本不能在 O(n log n) 时间内实现,只有 O(1) 指针,因为它需要 O(1) 跨步访问任意步幅大小。
  • @j_random_hacker:跨步访问没问题;在每次搜索长度为 k 之后,您访问 2k 个连续元素。我的观点是,根据您对计算机所做的假设,有两种非常不同的方法可以达到给定的 O(log n) 空间界限。
  • @tmyklebu:我遵循你的第一句话,但这只是让我相信链表可以在 O(n log n) 时间和 O(1) 额外空间内进行归并排序,并且声称因此,OP 链接到的页面是错误的!您的第二句话(以及您的原始评论)充其量仍然是非常具有误导性的,因为它暗示了链表上合并排序的迭代和递归形式的空间使用之间的等价性,当(正如您刚刚展示的那样!)前者严格更好如果我们将苹果与苹果进行比较。
【解决方案2】:

归并排序算法是递归的,因此它需要 O(log n) 的堆栈空间,无论是数组还是链表的情况。但是数组情况也分配了额外的 O(n) 空间,它支配了堆栈所需的 O(log n) 空间。所以数组版本是O(n),链表版本是O(log n)。

【讨论】:

  • 为什么 O(n) 优于 O(logn)?我虽然合并排序在使用数组时会有 n + log(n) 复杂性? n -> 子数组和 log(n) 调用堆栈?
  • @nhoxbypass O(n + log n) 仍然是 O(n),因为我们去掉了低阶项。
猜你喜欢
  • 2020-12-14
  • 1970-01-01
  • 2017-11-12
  • 2021-08-13
  • 2018-03-19
  • 2011-09-03
  • 2022-10-24
  • 2011-12-09
  • 2011-07-09
相关资源
最近更新 更多