我把快排写错了?

众所周知,快速排序的时间复杂度是O(nlgn)O(n\lg n)的。然而因为我太菜,写出来的快速排序一不小心就成了O(n2)O(n^2)

这个辣鸡问题

这个应该都知道,当待排序数组已经有序时,固定选择某一个位置的数字当哨兵的快排会变成O(n2)O(n^2)。一个解决方法就是随机选择哨兵,或者干脆将输入的数组打乱后再排序。

然而,如果输入的数组数字全部相同呢?

当然这在实际中很少见,但是在实现的时候就要小心。在这种情况下,所实现的快排必须对于相同数字也交换位置,否则就会退化为O(n2)O(n^2)

比如说,这个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void quicksort(int left,int right)
{
int i,j,t,temp;
if(left>right)
return;

temp=a[left]; //temp中存的就是基准数
i=left;
j=right;
while(i!=j)
{
//顺序很重要,要先从右边开始找
while(a[j]>=temp && i<j)
j--;
//再找右边的
while(a[i]<=temp && i<j)
i++;
//交换两个数在数组中的位置
if(i<j)
{
t=a[i];
a[i]=a[j];
a[j]=t;
}
}
//最终将基准数归位
a[left]=a[i];
a[i]=temp;

quicksort(left,i-1);//继续处理左边的,这里是一个递归的过程
quicksort(i+1,right);//继续处理右边的 ,这里是一个递归的过程
}

还有这个,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void quick_sort(int s[], int l, int r)
{
if (l < r)
{
//Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 参见注1
int i = l, j = r, x = s[l];
while (i < j)
{
while(i < j && s[j] >= x) // 从右向左找第一个小于x的数
j--;
if(i < j)
s[i++] = s[j];

while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数
i++;
if(i < j)
s[j--] = s[i];
}
s[i] = x;
quick_sort(s, l, i - 1); // 递归调用
quick_sort(s, i + 1, r);
}
}

它们都会跳过相同的数字,每次排序后哨兵总会在边界上,导致算法劣化到O(n2)O(n^2)

大概就是这回事,没别的了。这种情况当然有改进的快速排序可以直接避免这种罕见的情况,在不大幅度改动算法的前提下,就要对相同的元素也进行换位才可以,即使会增加交换次数。


我把快排写错了?
https://blog.chenc.me/2019/07/11/do-i-write-qsort-right/
作者
CC
发布于
2019年7月12日
许可协议