1.快速排序算法的描述
1.1快速排序的方法是每一次把数组分成两个部分,其中大于K(从数组里面选出来的标准元素,一般是第一个)的在前面,小于K的在后面。然后对两个数组做同样的事情,直到这两个数组只有一个元素那么排序完成
因此快速排序可以分成两个部分,一个是分治,就是不断递归,减小问题规模;另一个就是数据的移动(挖坑填补)
代码描述,在这里做分治的主要操作
/**
* @description 快速排序,分治部分
*
*/
public static void quickSort(int[] list,int lo,int hi){
if(lo>=hi)return;
int index=pair(list,lo,hi);
quickSort(list,lo,index-1);
quickSort(list,index+1,hi);
}
1.2如何进行切分(数据移动)
第一次取下标为0的元素作为k,两个指针,i指向第一个元素,j指向最后一个元素
假设k被从数组中挖了出来,然后需要一个数填补空位,那么j从后往前移动,找到一个比k小的,然后和k交换位置;接下来就i向前移动,找到比k大的,然后再和k交换位置,这样就完成了一个循环
如此往复,知道i和j相遇活着一方走到尽头,那么切分就结束了
2.代码呈现
需要说明的地方:
- 使用了一个随机数组生成器,然后使用了断言来判断是否排序成功。这一步值得注意的地方有两个,一个是数组的赋值用等于是引用传递,这里需要调用对象的clone()方法来实现值传递,另一个是为了开启断言编译的时候需要加上-ea这个选项,在idea里面的操作步骤为,Run->Edit Configurations->VM options 加上 -ea,数组的比较需要使用Arrays.equals()
- 整个快速排序分成两个方面,一个是递归分成小问题,一个是分割,用来调整数据位置
- 在分割的时候,直接遍历查找i,j然后直接把i,j进行交换。最后跳出循环的时候,交换j和第一个位置,因为j所指向的都是小于或等于第一个的,j要从hi+1开始因为--操作会先执行,而i可以从lo开始因为第一个没有必要检测。
import java.util.Arrays;
/**
* Created by kisstheraik on 16/8/13.
* Description 快速排序
*/
public class QuickSort{
public static void main(String[] args){
int[] l=getList(6);
int[] t=l.clone();
System.out.println("原数组:");
for (int tmp:l)
System.out.print(tmp + " ");
Arrays.sort(t);
quickSort(l, 0, l.length-1);
//用来判断排序有没有成功
assert Arrays.equals(l,t):"排序没有成功";
System.out.println("\n排序后:");
for (int tmp:l)
System.out.print(tmp + " ");
}
/**
* @description 快速排序,分治部分
*
*/
public static void quickSort(int[] list,int lo,int hi){
if(lo>=hi)return;
int index=pair(list,lo,hi);
quickSort(list,lo,index-1);
quickSort(list,index+1,hi);
}
/**
*
* @param list int[] 要排序的数组
* @param lo 最低位置
* @param high 最高位置
* @return 标准元素的下标
* @description 快速排序,分割部分
*/
public static int pair(int [] list,int lo,int high){
int first=list[lo];
int i,j;
i=lo;
j=high+1;
while (true){
while (list[--j]>first)if(j==lo)break;
while(list[++i]<first)if(i==high)break;
if(i>=j)break;
int tmp=list[i];
list[i]=list[j];
list[j]=tmp;
}
list[lo]=list[j];
list[j]=first;
return j;
}
/**
*
* @param length int 数组的长度
* @return int[] 随机数组
*/
public static int[] getList(int length){
int[] list=new int[length];
for(int i=0;i<length;i++)
list[i]=(int)(Math.random()*100);
return list;
}
}
<?php
/**
* Created by PhpStorm.
* User: kisstheraik
* Date: 16/8/13
* Time: 下午11:10
*/
//分治
function quickSork(&$array,$lo,$hi){
if($lo>=$hi)return;
$index=par($array,$lo,$hi);
quickSork($array,$lo,$index-1);
quickSork($array,$index+1,$hi);
}
//分割
function par(&$array,$lo,$hi){
$first=$array[$lo];
$i=$lo;
$j=$hi+1;
while(true){
while($array[--$j]>$first)if($j==$lo)break;
while($array[++$i]<$first)if($i==$hi)break;
if($i>=$j)break;
$tmp=$array[$j];
$array[$j]=$array[$i];
$array[$i]=$tmp;
}
$array[$lo]=$array[$j];
$array[$j]=$first;
return $j;
}
$array=[23,43,13,6,1,7,89];
quickSork($array,0,6);
foreach($array as $k=>$v)
echo $v." ";
3.优点和缺点的分析
由于内循环的比较次数较少,因此效率高,最高的时候是每次都能把数组分成均等的两个,但是最坏的情况就是k为最小的或者最大的元素,这时候性能达到n^2
4.复杂度的计算
主定理: T [n] = aT[n/b] + f (n)
其中 a >= 1 and b > 1 是常量 并且 f (n) 是一个渐近正函数, 为了使用这个主定理,您需要考虑下列三种情况:
上面的定理用于分析递归问题的复杂度 a为子问题的个数,n/b为子问题的规模,快速排序里面最好的时候f(n)=O(n),满足第二个,T[n]=O(nlogn),因此最好的情况是nlogn.
最坏的情况是O(n^2),具体的证明,参考《算法导论》
5.如何进行优化
5.1小的数组的时候切换到插入排序
插入排序:
插入排序的核心思想是。把一个数插入一个已经排好的序列里面,一直和前一个元素比较,不符合大小就交换,直到插入了相应的位置
import java.util.Arrays;
/**
* Created by kisstheraik on 16/8/13.
*/
public class InsertSort {
public static void main(String[] args){
int[] l=getList(10);
int[] t=l.clone();
System.out.println("原数组:");
for (int tmp:l)
System.out.print(tmp + " ");
Arrays.sort(t);
insertSort(l);
//用来判断排序有没有成功
assert Arrays.equals(l,t):"排序没有成功";
System.out.println("\n排序后:");
for (int tmp:l)
System.out.print(tmp + " ");
}
public static void insertSort(int []list){
int length=list.length;
//对数组进行插入排序
for(int i=1;i<length;i++){
//把list[i]插入到0-i这个数组里面
int j=i;
while(j>0&&list[j]<list[--j]){
//交换位置 j 和j-1
int t=list[j];
list[j]=list[j+1];
list[j+1]=t;
}
}
}
/**
*
* @param length int 数组的长度
* @return int[] 随机数组
*/
public static int[] getList(int length){
int[] list=new int[length];
for(int i=0;i<length;i++)
list[i]=(int)(Math.random()*100);
return list;
}
}
同时使用快速排序和插入排序:这里一定注意需要return不能丢
/**
* @description 快速排序,分治部分
*
*/
public static void quickSort(int[] list,int lo,int hi){
if((lo+5)>=hi){
InsertSort.insertSort(list,lo,hi);
return;
}
//if(lo>=hi)return;
int index=pair(list,lo,hi);
quickSort(list,lo,index-1);
quickSort(list,index+1,hi);
}
两个版本(有插入排序和没有插入排序)的结果分析
1亿数据排序的结果
没有插入排序 | 20072ms | 16711ms | 17047ms | 15084ms |
有插入排序,最小为5 | 15706ms | 15612ms | 17790ms | 14362ms |
可以看出增加的插入排序还是加快了排序速度
5.2使用经过筛选的k,k可以取为3,影响效率的主要因素就是根据k进行数组分组的过程
//比较复杂留到以后分析