能否将快速排序的最坏情况时间复杂度优化到 O(nLogn)?

快速排序典型实现的最坏情况时间复杂度是 O(n2)。最坏情况发生在选取的基准值总是极值(最小或最大元素)时。当输入数组已经排序或反向排序,并且我们总是选取第一个或最后一个元素作为基准时,就会出现这种情况。

虽然随机化快速排序在数组有序时也能表现良好,但随机选取的元素仍然可能总是极值。我们能否将最坏情况降低到 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 https://www.geeksforgeeks.org/dsa/kth-smallest-largest-element-in-unsorted-array-worst-case-linear-time/

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 < n / 5; i++)

median[i] = findMedian(arr + l + i * 5, 5);

if (i * 5

< n) // For last group with less than 5 elements

{

median[i] = findMedian(arr + l + i * 5, n % 5);

i++;

}

// Find median of all medians using recursive call.

// If median[] has only one element, then no need

// of recursive call

int medOfMed = (i == 1) ? median[i – 1]

: kthSmallest(median, 0,

i – 1, i / 2);

// Partition the array around a random element and

// get position of pivot element in sorted array

int pos = partition(arr, l, r, medOfMed);

// If position is same as k

if (pos – l == k – 1)

return arr[pos];

if (pos – l

> 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 < r; i++)

if (arr[i] == x)

break;

swap(&arr[i], &arr[r]);

// Standard partition algorithm

i = l;

for (int j = l; j <= r – 1; j++) {

if (arr[j] <= x) {

swap(&arr[i], &arr[j]);

i++;

}

}

swap(&arr[i], &arr[r]);

return i;

}

/ Function to print an array /

void printArray(int arr[], int size)

{

int i;

for (i = 0; i < size; i++)

cout << arr[i] << " ";

cout << endl;

}

// Driver program to test above functions

int main()

{

int arr[] = { 1000, 10, 7, 8, 9, 30, 900

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/52415.html
点赞
0.00 平均评分 (0% 分数) - 0