快排性能的分析
决定快排性能的关键是每次单趟排序后,key对于数组的划分,如果key每次都能基本剧中二分,那么快排的递归树就是一棵均匀的满二叉树,性能也就最好。但是如果每次选的key都是最大值或者最小值那么每一次只能划分成0个跟n-1个子问题,时间复杂度就是 O(n^2)
,数组序列有序时就会出现这样的状况,可以使用三数取中或者随机选key解决这个问题,但是如果这个带排序的数组中有很多重复的数据,那么也是无法得到很好的解决的。
三路划分
当面对大量跟key值相等的值的时候,三路划分的核心思想有点类似hoare的左右指针跟lomuto前后指针的结合。核心思想就是把数组分成三段,1.比key小的值 2.跟key相等的值 3.比key大的值。
1.key默认取left位置的值
2.left指向数组的最左侧,right指向数组的最右侧。cur指向left的下一个。
3.判断cur指向的数据的值跟key比较
如果cur指向的值大于key,那么将cur指向的值跟right指向的值交换,然后right--;
如果cur指向的值小于key,那么将cur指向的值跟left指向的值交换,然后left++,cur++;
如果cur指向的值等于key,那么cur++;
这样可以保证我们cur左边的值一定先是跟key相等的值,left跟cur之间的都是跟key相等的值,再left左边都是比key小的值,在cur右边都是比key大的值。然后再递归左右两个区间
4.直到cur>right结束。
单趟之后:
下面是代码实现(带有备注的):
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<assert.h>
void swap(int* a, int* b)//这是交换函数
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void myqsort(int* ch,int left, int right)//left代表最左边元素的数组下标,right代表最右边元素的数组下标
{
if (left < right)
{
int Left = left;
int Right = right;//记录位置
int key = ch[left];
int cur = left + 1;
while (cur <= right)
{
if (ch[cur] > key)
{
swap(&ch[cur], &ch[right]);
right--;
}
else if (ch[cur] < key)
{
swap(&ch[cur], &ch[left]);
left++;
cur++;
}
else
cur++;
}
myqsort(ch, Left, left - 1);
myqsort(ch, right + 1, Right);
}
else
return;
}
int main()
{
int ch[100010] = { 0 };
int n = 0;
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &ch[i]);
myqsort(ch, 0, n - 1);
for (int i = 0; i < n; i++)
printf("%d ", ch[i]);
return 0;
}
这样优化好像无法通过洛谷上的排序,感兴趣的可以去试试:P1177 【模板】排序 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
这是leetcode上的排序链接:
还是很好理解的。