【问题标题】:How is it possible to do binary search on a singly-linked list in O(n) time?如何在 O(n) 时间内对单链表进行二分查找?
【发布时间】:2013-11-02 11:51:12
【问题描述】:

This earlier question 谈论在 O(n) 时间内对双向链表进行二进制搜索。该答案中的算法如下工作:

  • 转到列表中间进行第一次比较。
  • 如果它等于我们正在寻找的元素,我们就完成了。
  • 如果它比我们要查找的元素大,请向后走到起点的一半并重复。
  • 如果它比我们要查找的元素小,则向前走一半到起点并重复。

这对于 双向 链表非常有效,因为它可以向前和向后移动,但该算法不适用于单链表。

是否有可能在 O(n) 时间内对单链表而不是双向链表进行二分查找?

【问题讨论】:

  • 我读过它,在这种情况下,很明显,回答问题的意思是:“如果您提出问题然后找到解决方案,请随时回答该问题。”绝对没有这样的:“如果您已经知道答案,请创建问题,然后立即回答问题。”
  • @libik-“提问页面”上有一个明确的复选框,专门允许您同时提问和回答问题。我几乎肯定这是专为此类情况设计的,尽管我可能是错的。
  • 这有什么意义?线性搜索是O(n),所以如果你的二分搜索也是O(n),那么复杂度是一样的。
  • @Barmar 的优点是,就像下面 cmets 中提到的 templatetypedef,在比较非常昂贵的情况下,比遍历列表要多得多。
  • @templatetypedef 不难看出你是个好人而且你的意图是好的。请忽略“背景噪音”;)

标签: algorithm data-structures linked-list big-o binary-search


【解决方案1】:

这可以通过使用双指针方法来实现(前提是列表按排序顺序),如本研究工作中所述:http://www.ijcsit.com/docs/Volume%205/vol5issue02/ijcsit20140502215.pdf

【讨论】:

  • 对于经过同行评审的研究论文来说这已经足够的想法令人惊讶。
【解决方案2】:

完全有可能做到这一点。实际上,您几乎只需对双向链表算法进行一项更改即可使其正常工作。

单链表的问题是,如果你有一个指向列表中间的指针,你就不能向后返回到列表的第一季度。但是,如果您考虑一下,您不需要从中间开始执行此操作。相反,您可以从列表的前面开始,然后走到第一季度。这需要(基本上)与以前相同的时间:您可以从前面开始,然后向前走 n / 4 步,而不是向后退 n / 4 步。

现在假设您已经完成了第一步并且在位置 n / 4 或 3n / 4。在这种情况下,如果您需要备份到位置 n / 8,您将遇到与以前相同的问题或位置 5n / 8。如果你需要到达位置 n / 8,你可以再次从列表的最前面开始,向前走 n / 8 步。 5n / 8的情况如何?这是诀窍 - 如果您仍然有指向 n / 2 点的指针,那么您可以从那里开始向前走 n / 8 步,这将带您到正确的位置。

更一般地,不是存储指向列表中间的指针,而是将 两个 指针存储到列表中:一个在值可能所在的范围的前面,一个在值的中间值可能在的范围。如果您需要在列表中向前推进,请将指向范围开始的指针更新为指向范围中间的指针,然后将指向范围中间的指针向前移动到范围结束的一半。如果需要在列表中向后前进,将指向范围中间的指针更新为指向范围前面的指针,然后向前走一半。

总的来说,这与双向链接的情况具有完全相同的时间复杂度:我们采取 n / 2 步,然后是 n / 4 步,然后是 n / 8 步,等等,总和为 O(n)脚步。我们也只进行 O(log n) 的总比较。唯一的区别是我们需要跟踪的额外指针。

希望这会有所帮助!

【讨论】:

    【解决方案3】:

    比较需要 O(1),需要更多时间的是遍历节点。因此,即使您持有指向 n/2、n/4 和 3n/4 的指针,您找到它所需的时间仍然是 O(n)。

    此外,如果您从中间开始并返回(或向前),您不妨沿途比较,因为进行另一次比较需要相同的时间:O(1)。

    总结一下:
    在链表上运行二进制搜索是没有意义的,除非链表由一个数组 (ArrayList) 支持,该数组允许在 O(1) 中直接访问其元素。

    【讨论】:

    • 如果比较成本很高,那么使用二分搜索而不是常规搜索实际上有很大的好处。所采取的步骤数量会稍微多一些,但比较的数量会成倍减少。如果比较花费的时间比在列表中前进的时间要长得多,那么进行二分搜索会有很大的好处。
    • 这如何回答这个问题?问题不在于它是否可能在少于O(n) 或是否有意义。问题是在O(n) 中是否以及如何实现。
    • 计算哈希码不太可能比直接比较更有效(例如,在字符串上调用哈希码会遍历整个字符串,因此等于比较实际上可能更有效,因为它可以短路)。只有在比较之前已经计算过哈希码,才能节省时间。
    • a) 这里没有答案。 b) 它没有用。它仅包含每个人都已经知道的信息(以及您的个人意见)。
    • @Dukeling a) 在 cmets 中明确说明 b) 在过去的 15 年中,我什至没有遇到过一次比较如此昂贵的情况,如理论上所述,此外,当比较 is 昂贵 - 您不会将数据保存在链表中。 c)我不会改变我的答案,即使知道如何在单链表上执行二分搜索很酷——我相信警告人们“不要在家尝试”并提供一个好主意合理解释原因。
    猜你喜欢
    • 2013-11-02
    • 2022-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-06
    相关资源
    最近更新 更多