如何理解快速排序的時間複雜度是O(nlogn)

選擇排序、冒泡排序等算法的時間複雜度都比較好理解,但不是很清楚快速排序的時間複雜度爲什麼是O(nlogn)。從《算法圖解》中看到的思路,很贊,解決了一直以來的疑惑。

引用自《算法圖解》:

快速排序的情況比較棘手,在最糟情況下,其運行時間爲O(n2)。。在平均情況下,快速排序的運行時間爲O(nlogn)。

1、平均情況與最糟情況

快速排序的性能高度依賴於你選擇的基準值。

  • 最糟情況
    假設你總是將第一個元素用作基準值,且要處理的數組是有序的。由於快速排序算法不檢查輸入數組是否有序,因此它依然嘗試對其進行排序。注意,數組並沒有被分成兩半,相反,其中一個子數組始終爲空,這導致調用棧非常長在這裏插入圖片描述
  • 平均情況
    假設你總是將中間的元素用作基準值,在這種情況下,調用棧如下。 調用棧短得多!因爲你每次都將數組分成兩半,所以不需要那麼多遞歸調用。你很快就到達 了基線條件,因此調用棧短得多。
    在這裏插入圖片描述
    第一個示例展示的是最糟情況,而第二個示例展示的是最佳情況。在最糟情況下,棧長爲 O(n),而在最佳情況下,棧長爲O(log n)。

現在來看看棧的第一層。你將一個元素用作基準值,並將其他的元素劃分到兩個子數組中。 這涉及數組中的全部8個元素,因此該操作的時間爲O(n)。在調用棧的第一層,涉及全部8個元素, 但實際上,在調用棧的每層都涉及O(n)個元素。
在這裏插入圖片描述
即便以不同的方式劃分數組,每次也將涉及O(n)個元素。
在這裏插入圖片描述
在這個示例中,調用棧的高度爲O(log n),而每層需要的時間爲O(n)。因此整個算法需要的時間爲O(n) * O(log n) = O(n log n)。這就是最佳情況。 在最糟情況下,有O(n)層,因此該算法的運行時間爲O(n) * O(n) = O(n2)。

2、歸併排序和快速排序的時間複雜度都是 O(nlogn),爲什麼說快速排序一般優於歸併排序?

首先搬運stackOverflow的回答:Why is mergesort better for linked lists?

One of the main sources of efficiency in quicksort is locality of reference, where the computer hardware is optimized so that accessing memory locations that are near one another tends to be faster than accessing memory locations scattered throughout memory. The partitioning step in quicksort typically has excellent locality, since it accesses consecutive array elements near the front and the back. As a result, quicksort tends to perform much better than other sorting algorithms like heapsort even though it often does roughly the same number of comparisons and swaps, since in the case of heapsort the accesses are more scattered.
Additionally, quicksort is typically much faster than other sorting algorithms because it operates in-place, without needing to create any auxiliary arrays to hold temporary values. Compared to something like merge sort, this can be a huge advantage because the time required to allocate and deallocate the auxiliary arrays can be noticeable. Operating in-place also improves quicksort’s locality.
When working with linked lists, neither of these advantages necessarily applies. Because linked list cells are often scattered throughout memory, there is no locality bonus to accessing adjacent linked list cells. Consequently, one of quicksort’s huge performance advantages is eaten up. Similarly, the benefits of working in-place no longer apply, since merge sort’s linked list algorithm doesn’t need any extra auxiliary storage space.
That said, quicksort is still very fast on linked lists. Merge sort just tends to be faster because it more evenly splits the lists in half and does less work per iteration to do a merge than to do the partitioning step.

翻譯一下:
快速排序中效率的主要來源之一是引用位置,在引用位置中,計算機硬件經過優化,因此訪問彼此相鄰的內存位置往往比訪問分散在整個內存中的內存位置更快。quicksort中的分區步驟通常具有很好的局部性,因爲它訪問前面和後面附近的連續數組元素。因此,快速排序往往比其他排序算法(如heapsort)執行得更好,儘管它通常執行大致相同數量的比較和交換,因爲在heapsort的情況下,訪問更加分散。

此外,quicksort通常比其他排序算法快得多,因爲它在原地運行,而不需要創建任何輔助數組來保存臨時值。與merge sort相比,這是一個巨大的優勢,因爲分配和釋放輔助數組所需的時間是顯而易見的。就地操作也提高了quicksort的位置。

使用鏈表時,這兩個優點都不一定適用。由於鏈表單元通常分散在整個內存中,因此訪問相鄰的鏈表單元沒有額外的局部性好處。因此,quicksort的一個巨大的性能優勢被消耗殆盡。類似地,就地工作的好處不再適用,因爲merge sort的鏈表算法不需要任何額外的輔助存儲空間。

也就是說,快速排序在鏈接列表中仍然非常快。合併排序往往更快,因爲它更均勻地將列表分成兩半,並且每次執行合併所做的工作比執行分區步驟所做的工作更少。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章