为啥以下算法(循环排序?!)的时间复杂度是 O(n)?

Posted

技术标签:

【中文标题】为啥以下算法(循环排序?!)的时间复杂度是 O(n)?【英文标题】:Why time complexity of following algorithm(cycle sort?!) is O(n)?为什么以下算法(循环排序?!)的时间复杂度是 O(n)? 【发布时间】:2019-09-03 02:45:49 【问题描述】:

循环排序的时间复杂度是O(n^2)reference

但是,该解决方案声称以下涉及循环排序的算法仅使用 O(n)。时间复杂度不应该是 O(n^2) 吗?

    def find_all_missing_numbers(nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        i = 0

        # This is cycle sort and should use O(n^2)?!
        while i < len(nums):
            j = nums[i] - 1
            if nums[i] != nums[j]:
                nums[i], nums[j] = nums[j], nums[i]  # swap
            else:
                i += 1

        missingNumbers = []

        # O(n)
        for i in range(len(nums)):
            if nums[i] != i + 1:
                missingNumbers.append(i + 1)

        return missingNumbers
# 

时间复杂度 = O(n^2 + n) = O(n^2)。解错了吗?

【问题讨论】:

用不同 n 的示例简单地分析代码以查看它是线性的还是二次的呢? 【参考方案1】:

这不是循环排序,该算法旨在在数组包含[1, len(array)] 范围内的数字时查找缺失的数字。

print(find_all_missing_numbers([5,4,3,2,1]))
print(find_all_missing_numbers([1,2,3,5,5]))
print(find_all_missing_numbers([2]))

[] [4] 错误

这一行假设正确的位置是由一个存储的数字给出的,只有当数字在上面显示的范围内时它才有效。

j = nums[i] - 1

虽然循环排序会花费线性时间为每个数字寻找合适的位置。

【讨论】:

在循环排序中,它不是使用j=nums[i] - 1,而是使用迭代循环j++ 来找到nums[i] 的校正位置,这将花费O(n),因此需要O(n^ 2)循环排序 @jen007 没错。这就是原因。如果问题中的过程在[1, len(array)] 范围内具有不同的所有不同数字的数组,则会对其进行排序。否则可能无法排序。 不会有最多 n 次交换,因此循环排序时间复杂度为 O(n)? cs.stackexchange.com/a/118802/121682 "最多有 n 个项目在错误的位置。每次交换交换两个都在错误位置的元素,并将至少一个移动到正确的位置。经过 n 次交换后,所有元素都在正确的位置,并且此时不会有进一步的交换。因此最多有 n 次交换。” @AhmadFerdous 不确定您的评论与我的回答有何关联 @Yola 抱歉,我的评论不清楚。您提到,“循环排序花费线性时间为每个数字寻找合适的位置。”对于数组大小为 n 的所有数字(1 到 n),不会有总共 n 个交换吗?该参考资料解释了它如何总计为 n 次交换。【参考方案2】:
def find_all_missing_numbers(nums):

        i = 0 # Will happen only once so O(1)

        # no linear search for true position
        while i < len(nums):
            j = nums[i] - 1 # Will happen once per each iteration
            if nums[i] != nums[j]: # Condition Check Will happen once per iteration
                nums[i], nums[j] = nums[j], nums[i] # Will happen if if Condition is true so "c" times
            else: 
                i += 1 # Will happen if if Condition is not true so "c'" times

        missingNumbers = []

        # O(n)
        for i in range(len(nums)):
            if nums[i] != i + 1:
                missingNumbers.append(i + 1)

        return missingNumbers

所以:

1 + Len(nums)*(1 + 1 + c + c' + 1) + n

如果 Len(nums) = n 那么

1 + 3n + (c + c')n + n = 1 + (3+C)n + n ~ O(n)

【讨论】:

以上是关于为啥以下算法(循环排序?!)的时间复杂度是 O(n)?的主要内容,如果未能解决你的问题,请参考以下文章

时间/空间复杂度,基础排序算法(冒泡选择快速插入)

算法-排序

排序算法

十大排序算法之选择排序

算法 基础

常见查找和排序算法