[英]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.