快速排序 简单理解

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(nlog⁡n) 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

以上是关于快速排序 简单理解的主要内容,如果未能解决你的问题,请参考以下文章

java算法面试题:设计一个快速排序。双路快速排序,简单易于理解。

快速排序就这么简单

排序算法(快速排序)

从一道简单算法题理解快速排序的 partition 操作

快速排序和冒泡排序

算法理解—— 快速排序 v2.0