📜  可以在O(nLogn)最坏情况下的时间复杂度中实现QuickSort吗?

📅  最后修改于: 2021-05-04 09:06:23             🧑  作者: Mango

典型的QuickSort实现的最坏情况下的时间复杂度是O(n 2 )。最坏的情况发生在拾取的轴始终是极端(最小或最大)元素时。当对输入数组进行排序或反向排序并且选择第一个或最后一个元素作为枢轴时,会发生这种情况。

尽管即使对数组进行排序,随机化QuickSort仍能很好地工作,但仍然有可能随机选择的元素始终是极端的。最坏的情况可以减少为O(nLogn)吗?

答案是肯定的,我们可以实现O(nLogn)最坏的情况。这个想法是基于这样一个事实,即未排序数组的中值元素可以在线性时间内找到。因此,我们首先找到中位数,然后围绕中位数元素对数组进行分区。
以下是基于上述思想的C++实现。下面程序中的大多数功能都是从“未排序数组”中的第K个最小/最大元素复制而来的。组合3(最坏情况的线性时间)

C++
/* A worst case O(nLogn) implementation of quicksort */
#include
#include
#include
#include
using namespace std;
 
// Following functions are taken from http://goo.gl/ih05BF
int partition(int arr[], int l, int r, int k);
int kthSmallest(int arr[], int l, int r, int k);
 
/* A O(nLogn) time complexity function for sorting arr[l..h] */
void quickSort(int arr[], int l, int h)
{
    if (l < h)
    {
        // Find size of current subarray
        int n = h-l+1;
 
        // Find median of arr[].
        int med = kthSmallest(arr, l, h, n/2);
 
        // Partition the array around median
        int p = partition(arr, l, h, med);
 
        // Recur for left and right of partition
        quickSort(arr, l, p - 1);
        quickSort(arr, p + 1, h);
    }
}
 
// A simple function to find median of arr[].  This is called
// only for an array of size 5 in this program.
int findMedian(int arr[], int n)
{
    sort(arr, arr+n);  // Sort the array
    return arr[n/2];   // Return middle element
}
 
// Returns k'th smallest element in arr[l..r] in worst case
// linear time. ASSUMPTION: ALL ELEMENTS IN ARR[] ARE DISTINCT
int kthSmallest(int arr[], int l, int r, int k)
{
    // If k is smaller than number of elements in array
    if (k > 0 && k <= r - l + 1)
    {
        int n = r-l+1; // Number of elements in arr[l..r]
 
        // Divide arr[] in groups of size 5, calculate median
        // of every group and store it in median[] array.
        int i, median[(n+4)/5]; // There will be floor((n+4)/5) groups;
        for (i=0; i k-1)  // If position is more, recur for left
            return kthSmallest(arr, l, pos-1, k);
 
        // Else recur for right subarray
        return kthSmallest(arr, pos+1, r, k-pos+l-1);
    }
 
    // If k is more than number of elements in array
    return INT_MAX;
}
 
void swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}
 
// It searches for x in arr[l..r], and partitions the array
// around x.
int partition(int arr[], int l, int r, int x)
{
    // Search for x in arr[l..r] and move it to end
    int i;
    for (i=l; i


输出:

Sorted array is
1 5 6 7 8 9 10 20 30 900 1000

在实践中如何实现QuickSort –是否使用上述方法?
尽管上述方法的最坏情况下时间复杂度为O(nLogn),但在实际实现中从未使用过。与常规Quicksort相比,此方法中的隐藏常数较高。以下是QuickSort的实际实现中使用的一些技术。
1)随机捡拾,使发生最坏情况的可能性降低(随机化QuickSort)
2)对小型数组进行插入插入排序,以减少递归调用。
3)QuickSort是尾部递归的,因此完成了尾部调用优化。

因此,以上讨论的方法更多是具有O(nLogn)最坏情况时间复杂度的理论方法。