簡體   English   中英

近排序數組的插入排序最壞時間復雜度?

[英]insertion sort worst time complexity for near sorted array?

我有一個 n 元素數組。 4√n之外的所有元素都被排序。 我們不知道這些錯位元素的位置。 對此列表進行排序的最有效方法是什么?

有沒有 O(n) 的方法來做到這一點?

更新 1:

對於幾乎排序的數據,插入排序的時間復雜度是 O(n)(在最壞的情況下是真的嗎?)?

有一種對幾乎已排序的數組進行排序的快速通用方法:

  1. 從頭到尾掃描原始數組。 如果您發現兩個項目的順序不正確,請將它們移動到第二個數組並從第一個數組中刪除它們。 當心; 例如,如果刪除 x2 和 x3,則需要再次檢查 x1 ≤ x2。 這是在 O(n) 時間內完成的。 在您的情況下,新數組的大小最多為 8sqrt(n)。

  2. 對第二個數組進行排序,然后合並兩個數組。 由於第二個數組的項數較少,任何合理的排序算法都會在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 完成后,我將goodbad連接起來,然后讓 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM