最常用的排序–快速排序
以下讲解借鉴了啊哈算法一部分内容,如果有总结不到位的地方,恳请给位大佬批评指正
优点:相比较桶排序和冒泡排序来说,快速排序解决了桶排序的空间的浪费,和冒泡排序的执行效率
示例:
排列前
6 1 2 7 9 3 4 5 10 8
排列后
1 2 3 4 5 6 7 8 9 10
- 首先,我们先随便找一个数作为 基准数 ,在这里我们选择最左侧的 6 作为基准数。
- 对于现在我们需要做的就是把 基准数 6 移到之间的某一个位置,这里先提示一下,采用类似于 冒泡算法 (下次进行详解)的交换顺序,当然这里我们并不会对所有的数进行一个比较,而是选择,往下学,自然就懂了。
- 对序列的两端进行“探测”,定义两个哨兵 一个哨兵i,一个哨兵j,一个在序列的头部,一个在尾部。//也就是双指针
- 这里我们注意,哨兵j先移动。因为此时设置的的基准数为最左侧的数,所以是哨兵j先进行移动,这一点务必理解。哨兵j一步一步的向左移动(即j–),直到找到小于基准数6的数停下来。接下来的是哨兵i****向右进行移动(即i++),直到找到一个比基准数6大的数停下来。最后哨兵j停在了数字5前面,哨兵i停在了数组7前面。
- 然后进行一次交换。
- 到这里已经进行了一次交换
6 1 2 5 9 3 4 7 10 8
- 以上我们已经进行了一次交换,然后再进行上述方法
- 第二次交换进行结束,探测继续。哨兵j继续向左移动,他会发现数字3,满足条件之后停下来,哨兵i向右移动,然后和哨兵相遇。说明在此时,探测已经结束了。然后数字3和基准数6进行交换。
3 1 2 5 4 6 9 7 10 8
到了此时,第一次探测已经结束。
对刚刚的探测进行一次回顾:
- 此时以基准数6为分界线,6左边的为小于等于6的数字
- 6右边的数都大于等于6
- 哨兵j找到小于基准数的数
- 哨兵i找到大于基准数的数
- 直到哨兵i和哨兵j碰头为止
现在基准数已经归位了,现在我们将初始序列拆分成以原基准数为界线的两个序列
左边为
3 1 2 5 4
右边为
9 7 10 8
现在你可以试一试自己找一下基准数然后在画板上进行一次演练
对于左边选择3为基准数进行探测
交换后得到
2 1 3 5 4
以此类推
再分隔为左侧
2 1
右侧
5 4
交换后可得
1 2 3 4 5 6 9 7 10 8
对于初始基准数的右边可以分隔为
9 7 10 8
这里基准数选9
探测结束后可以得到
8 7 9 10
然后继续探测
左侧(右侧就一个10,没有必要进行分隔)
8 7
ok最后了,进排序可得
7 8
终于卸下一口气
全部排序完了
1 2 3 4 5 6 7 8 9 10
等你以上全部看懂后你可以自行写出图解,当然我这里也准备好了
以上就是快速排序的一个思想,以下就是代码演示
#include<stdio.h>
int a[101],n;//定义全局变量,这两个变量需在子函数中使用
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);
return;
}
int main(){
int i, j;
//读入数据
printf("输入需要排序的数字元素数量:\n");
scanf("%d",&n);
printf("输入需要排序的数字元素量:\n");
for(i = 1;i <= n; i++)
scanf("%d",&a[i]);
quicksort(1, n);//快速排序调用
//输出排序后的结果
for(i = 1; i <= n; i++){
printf("%d\t",a[i]);
}
return 0;
}
注意:
- 快速排序的最差时间复杂度和冒泡排序相同都是O(N^2)
- 但是平均的时间复杂度为O(NlongN)
- 基于二分思想实现