快速排序 简单理解
Posted 霜序0.2℃
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速排序 简单理解相关的知识,希望对你有一定的参考价值。
代码参考菜鸟教程
快排1
思想:
- 从数字中挑出一个基准数字
- 把基准数字左边和右边的数字进行划分,即比基准数字小的数字放基准数字左边,大的放右边
- 然后分治处理,在每一个基准分出的左边和右边都进行这样的操作
快速排序的思想不算难,就是代码实现起来有点麻烦
使用的时候记得是闭区间
核心代码:
int par(int l, int r) {
int v = arr[l];
while (l < r) {
while (l < r && arr[r] >= v) r--;//从右往左找,直到找到一个小于v的数字
arr[l] = arr[r];
while (l < r && arr[l] <= v) l++;//从左往右找,直到找到一个大于v的数字
arr[r] = arr[l];
}//如果对循环不够理解,可以画一个数轴来看,可以发现,他们可以算是封闭的,只需要最后的arr[l] = v进行赋值
arr[l] = v;
return l;
}
void quickSort(int l, int r) {
if (l < r) {
int p = par(l, r);
quickSort(l, p-1);
quickSort(p+1, r);
}
}
par
表示用来记录基准点并划分
以前都是直接用sort,现在手写快排反而写不出
代码思路是一方面,代码实现又是另一方面,我觉得发明这个算法的人挺厉害的
快排2
我在AcWing上面看到了另一种快排,y总的模板,现在贴出来
y总的模板,这种方式会将数组划分为小于等于和大于等于pivot 的左右两部分,并且每一部分都不会为空,因为每次递归的时候数组的长度都会变小从而确保不会死循环。如果每次都选取数组最左边的元素来作为pivot,当数组已经是有序的时候每次递归数组的长度只会减少一,导致时间复杂度变为 O ( n 2 ) O(n^2) O(n2),这可以通过选中间元素作为pivot或者每次随机选取数组中一个元素与最左边的元素交换来解决。注意这里不能用最右边的元素作为pivot,这样如果数组最右边是最大元素的话会导致划分完[l, j]不变导致死循环。
由于划分完成后pivot不一定在这两部分的分界线上,所以在做比如得到第k大的数这种题目时不能用j-l +1==k
来判断q[j]为第k大的数,因为左半区间只保证了所有数小于等于 pivot,而不一定都小于等于q[j]。
另外值得一提的是这种双向划分方式就是 Hoare 在最开始的论文中提出的方式 – Hoare Partition Scheme。
时间复杂度
平均时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn),最坏情况下 O ( n 2 ) O(n^2) O(n2),在数组已排好序的情况下出现,可以通过随机化或者取中点来避免最差情况。
void quick_sort(int q[], int l, int r) {
if (l >= r) return;
int x = q[l + r >> 1], i = l - 1, j = r + 1;
while (i < j) {
do i ++ ; while (q[i] < x);
do j -- ; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
quick_sort(q, l, j);
quick_sort(q, j + 1, r);
}
作者:Diamondz
原文链接:https://www.acwing.com/solution/content/2089/
解释一下代码,看半天差点没看出来i = l - 1, j = r + 1;
是用来干嘛的,是因为里面是do while
会先最先减一次
如果有疑问的可以看看这篇https://blog.csdn.net/SHU15121856/article/details/109839618
以上是关于快速排序 简单理解的主要内容,如果未能解决你的问题,请参考以下文章