数据结构与算法——排序算法之快速排序
-
快速排序定义
快速排序由C. A. R. Hoare在1962年提出通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
-
快速排序的优势
桶排序浪费空间严重,而冒泡排序解决了这一问题,但是在算法的执行效率上大打折扣。快速排序既解决了空间浪费问题,又使运算速度大幅提高,可谓一举双得。
-
自然语言描述(以降序为例)
- Step1: 选定一个最左或最右为基准数(本例选择最左)
- Step2: 定义两个标志变量,分别指向数据的两边有(以下简称左标和右标)。
- Step3: 右标开始向左移动,直到遇到小于基准数的数时停下来。
- Step4: 左标开始向右移动,直到遇到大于基准数的数时停下来。
- Step5: 将左右两标所指数字互换,重复第三四五步,直到左右两标指向同一数字,这三个步骤称为一趟。
- Step6 : 两标指向的同一数字与基准数互换完成快排。
- 代码实现:
[ 一 ] C语言实现
#include <stdio.h>
int a[101],n;//定义全局变量,这两个变量需要在子函数中使用
void quicksort(int left, int right);
int main()
{
int i,j;
//读入数据
scanf("%d",&n);
for(i=1; i<=n; i++)
scanf("%d",&a[i]);
quicksort(1,n);//快速排序调用
//输出排序后的结果
for(i=1; i<=n; i++)
printf("%d ",a[i]);
return 0;
}
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) //当哨兵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);//继续处理右边的,这里是一个递归的过程
}
- [ 二 ] java实现
public static int partition(int []array,int lo,int hi){
//固定的切分方式
int key=array[lo];
while(lo<hi){
while(array[hi]>=key&&hi>lo){//从后半部分向前扫描
hi--;
}
array[lo]=array[hi];
while(array[lo]<=key&&hi>lo){从前半部分向后扫描
lo++;
}
array[hi]=array[lo];
}
array[hi]=key;
return hi;
}
public static void sort(int[] array,int lo ,int hi){
if(lo>=hi){
return ;
}
int index=partition(array,lo,hi);
sort(array,lo,index-1);
sort(array,index+1,hi);
}
-
复杂度分析:
- 快速排序每次交换都是跳跃式的而不像冒泡每次都是相邻交换,交换距离得到拓展,快排最坏时间复杂度为O(N^2), 与冒泡的一样,因为快排比较与冒泡来说的优势就是交换距离扩展,最差情况下优势没了,自然就与之一样了。
- 快排的平均复杂度为O(NlogN)。
-
算法思想(分而治之):
1. 古代君王统治大片国土常用分治法,如我们所知的分封制,郡县制等等。采用分治法的原因很简单,1. 国土面积大,2.君王自身精力有限。
2. 同样我们面对规模较为复杂的问题时,可以借助于将其分为等价的规模较小的几个模块而解决,分而治之与软件设计的模块化思想有些类似,较大问题的解决都是基于较小问题的解决借助于递归而解决的。