![](/img/trans.png)
[英]Time complexity of finding range of target in sorted array - Is this solution O(N) in the worst case?
[英]insertion sort worst time complexity for near sorted array?
我有一个 n 元素数组。 除4√n
之外的所有元素都被排序。 我们不知道这些错位元素的位置。 对此列表进行排序的最有效方法是什么?
有没有 O(n) 的方法来做到这一点?
更新 1:
对于几乎排序的数据,插入排序的时间复杂度是 O(n)(在最坏的情况下是真的吗?)?
有一种对几乎已排序的数组进行排序的快速通用方法:
从头到尾扫描原始数组。 如果您发现两个项目的顺序不正确,请将它们移动到第二个数组并从第一个数组中删除它们。 当心; 例如,如果删除 x2 和 x3,则需要再次检查 x1 ≤ x2。 这是在 O(n) 时间内完成的。 在您的情况下,新数组的大小最多为 8sqrt(n)。
对第二个数组进行排序,然后合并两个数组。 由于第二个数组的项数较少,任何合理的排序算法都会在O(n)中对较小的第二个数组进行排序,合并又需要O(n),所以总时间为O(n)。
如果使用 O(n log n) 算法对第二个数组进行排序,那么只要错误位置的项数最多为 O(n / log n),排序就是 O(n)。
不,插入排序不是 O(n)。 最坏的情况是最后4√n 个元素错位了,而且它们太小以至于它们位于数组的前面。 需要插入排序 Θ(n √n) 才能将它们移到那里。
这是gnasher729 答案的 Python 实现,在这种接近排序的输入上是 O(n) 时间和 O(n) 空间。 但是,我们不能天真地从数组中“删除”对,这将是低效的。 相反,我将正确排序的值移动到一个good
列表中,将错误排序的对移动到一个bad
列表中。 所以只要数字在增加,它们就会被添加到good
。 但是如果下一个数字x
小于最后一个好数字good[-1]
,那么它们都被移动到bad
。 完成后,我将good
和bad
连接起来,然后让 Python 的Timsort完成剩下的工作。 它在 O(n - √n) 时间内检测已经排序good
运行,然后在 O(√n log √n) 时间内对bad
部分进行排序,最后在 O(n) 时间内合并两个已排序部分。
def sort1(a):
good, bad = [], []
for x in a:
if good and x < good[-1]:
bad += x, good.pop()
else:
good += x,
a[:] = sorted(good + bad)
接下来是空间改进版本,需要 O(n) 时间和 O(√n) 空间。 我没有将好的部分存储在额外的列表中,而是将其存储在a[:good]
:
def sort2(a):
good, bad = 0, []
for x in a:
if good and x < a[good-1]:
bad += x, a[good-1]
good -= 1
else:
a[good] = x
good += 1
a[good:] = bad
a.sort()
这是另一个 O(n) 时间和 O(√n) 空间变化,我让 Python 对我bad
,但我自己将好的部分与坏的部分从右到左合并。 所以这不依赖于 Timsort 的排序运行检测,因此很容易移植到其他语言:
def sort3(a):
good, bad = 0, []
for x in a:
if good and x < a[good-1]:
bad += x, a[good-1]
good -= 1
else:
a[good] = x
good += 1
bad.sort()
i = len(a)
while bad:
i -= 1
if good and a[good-1] > bad[-1]:
good -= 1
a[i] = a[good]
else:
a[i] = bad.pop()
最后,一些测试代码:
from random import random, sample
from math import isqrt
def sort1(a):
...
def sort2(a):
...
def sort3(a):
...
def fake(a):
"""Intentionally do nothing, to show that the test works."""
def main():
n = 10**6
a = [random() for _ in range(n)]
a.sort()
for i in sample(range(n), 4 * isqrt(n)):
a[i] = random()
for sort in sort1, sort2, sort3, fake:
copy = a.copy()
sort(copy)
print(sort.__name__, copy == sorted(a))
if __name__ == '__main__':
main()
输出,显示两个解决方案都通过了测试(并且测试有效,检测到fake
的不正确):
sort1 True
sort2 True
sort3 True
fake False
有趣的事实:对于 Timsort 单独(即,不用作上述算法的一部分),我上面提到的最坏情况是最好的情况:它会在 O(n) 时间内对其进行排序。 就像我第一个版本的sorted(good + bad)
,它会在 O(n - √n) 时间内识别 n-√n 个已排序元素的前缀,对 O(√n log √n) 中的 √n 最后一个元素进行排序) 时间,然后在 O(n) 时间内合并两个已排序的部分。
那么我们可以让 Timsort 做所有事情吗? 在所有这些接近排序的输入上都是 O(n) 吗? 不,这不对。 如果 4√n 个错位元素均匀分布在数组上,那么我们最多有 4√n 次排序运行,Timsort 将花费 O(n log(4√n)) = O(n log n) 时间来合并它们。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.