使用分治法从给定列表中查找第二小的数字

Posted

技术标签:

【中文标题】使用分治法从给定列表中查找第二小的数字【英文标题】:Finding the second smallest number from the given list using divide-and-conquer 【发布时间】:2013-10-25 07:11:35 【问题描述】:

我正在努力解决这个问题..

给定一个包含 n 个数字的列表,我们想找到最小的和第二小的 列表中的数字。描述一个分治算法来解决这个问题。假设整数 k 的 n = 2^k。使用您的算法的比较次数应该 不超过 3n/2 - 2,即使在最坏的情况下。

我目前的解决方案是使用选择算法得到中位数,然后将列表分为L1(包含小于或等于中位数的元素)、R(中位数)、L2(包含所有大于中位数的元素)。这是正确的吗?如果是这样,我下一步该怎么做?

【问题讨论】:

天真的方法效果很好:与 current_lowest 进行比较,(可能)与 current_2nd_lowest 进行比较。 @wildplasser 我同意这样的预期时间很好,但该声明特别要求最坏情况下的性能。最坏的情况可能是 [0,x,x,x,x,x,x,x,x] 之类的东西,您必须检查 0 之后的每个元素的最低和 second_lowest。它也不是分而治之。 @OP 您当前的解决方案是完全正确的。 :) 只需像这样继续划分列表并继续对具有较小元素的子列表进行操作。基本上对 L1 进行递归调用,直到 L1 最终成为仅包含 2 个元素的列表。请注意,它与使用快速选择算法 (k = 2) 结合中位数算法来选择枢轴非常相似。这种方法有很多信息,而且很容易实现。唯一困难的部分是理解子程序partition 是如何工作的。 【参考方案1】:

请注意,中值选择算法使用 Θ(n) 比较,但这并不意味着它最多使用 3n/2 - 2 次比较。事实上,我认为它使用的更多,这可能会排除您的解决方案策略。

作为提示:将此问题视为为所有 2k 建立淘汰赛;每轮的获胜者(两个数字中较小的一个)进入下一轮。实现它需要多少次比较?接下来,请注意第二小的数字必须“丢失”到最小的数字。第二小数也是“输”到最小数的最小数。鉴于此,你能有效地找到第二小的数字吗?

希望这会有所帮助!

【讨论】:

"也是最大的数“输”到了最小的数。" - 我想你的意思是第二小的数字是输给最小数字的最小数字。 谢谢,我实际上并没有在你的帖子中得到这个想法。好的,这个大纲怎么样:如果 n==2 比较并返回第一个,第二个最小的。如果 n>2 minimum= call(i=1 to n/2), and minimum2= call(i=n/2 to n) 并返回最小值......这可行吗? @Faisal- 你将如何返回两半的最小值?另外,如果您不理解我的帖子,也许我应该编辑它。有什么特别的事情你没有得到吗? @Faisal- 不,没有合并排序。您是否了解如何为 2^n 名玩家组织一场锦标赛,以便确定谁是总冠军? 很遗憾,我不明白如何为 2^n 名玩家组织锦标赛。【参考方案2】:

哦,我明白了(在 Python 中):

def two_min(arr):
    n = len(arr)
    if n==2: # Oops, we don't consider this as comparison, right?
        if arr[0]<arr[1]:                   # Line 1
            return (arr[0], arr[1])
        else:
            return (arr[1], arr[0])
    (least_left, sec_least_left) = two_min(arr[0:n/2])
    (least_right, sec_least_right) = two_min(arr[n/2:])
    if least_left < least_right:            # Line 2
        least = least_left
        if least_right < sec_least_left:    # Line 3
            return (least, least_right)
        else:
            return (least, sec_least_left)
    else:
        least = least_right
        if least_left < sec_least_right:    # Line 4
            return (least, least_left)
        else:
            return (least, sec_least_right)

总共:

第 1 行:将有准确的 n/2 比较

第 2 行:这里会有 n/4 + n/8 + ... + 1 比较

第 3 行和第 4 行:每次调用 two_min 时将执行其中的一个(除非使用两个元素调用它)。我们总共调用了two_minn-1 (因为有那么多锦标赛),其中n/2 被两个元素调用。所以第 3 行和第 4 行有助于n/2 - 1 比较

结合所有这些,我们有:

total_comparisons = n/2 + (n/4 + n/8 + ... + 1) + (n/2 - 1)
                  = (n - 1) + (n/2 - 1)
                  = 3n/2 - 2

【讨论】:

非常感谢您的帮助。

以上是关于使用分治法从给定列表中查找第二小的数字的主要内容,如果未能解决你的问题,请参考以下文章

查找最大和次大元素(JAVA版)(分治法)

算法导论第2章 分治法与归并排序, 二分查找法

分治法(实战篇之二分查找)

分治法

2.3.1 分治法

分治法