快速排序是在实践中运行比较快速的一种排序算法。它的平均时间性能为O(NlogN),但它的最坏的时间性能也是O()。像归并排序一样,快速排序是也是一种分治的递归算法。快速排序的算法思想是选取一个枢纽,根据这个枢纽可以将数组分为三个部分,小于的一组,等于的一组,大于的一组。然后对小于和大于的进行排序,排序好后和等于的三者进行连接得到最后排序好的数组。快速排序的经典实现算法为:
- 如果数组是0或者1个元素的时候直接返回
- 在数组中任意选取一个元素V作为枢纽
- 将剩余的元素分为两个集合小于等于的和大于等于的。
- 然后返回小于等于的后接上V在接上返回大的大于的。
在这里有两个问题需要注意:
- 枢纽的选择问题,如果选取第一个元素的话,那么如果预先输入的数组是将近有序的,那么第一个元素很可能就是相对较小的元素,那么整个排序将会出现一边倾斜的现象,将会是O(
)的时间复杂度。我们可以选择随机选择一个位置作为枢纽,这样是可行的,但是随机数的产生也会浪费时间。所以我们一般是选择left,center,right位置的元素进行一个排序,使这三个位置的元素是有序的,这个时候就选择center的位置作为枢纽,然后把这个center位置的元素和right-1位置的元素进行交换,然后递归的开始位置选择left+1和right-2。这样做不仅可以使选择枢纽是相对较中间的数,又可以为循环设置边界。
- 如果遇到了相等的元素,i和j的位置该不该停下来。设想一些如果全是是相等的元素,如果i和j都停下来,那么将会产生大量相同元素的交换,但是这个时候会产生两个平衡的数组,按照归并的思想,此时的时间复杂度是O(NlogN)。如果停止了,那么center的位置就变成了right-1或者right,也就是会产生两个极其不平衡的数组,这样的时间复杂度将会是O(
)。因此我们的做法还是要选择停下来进行交换。
- 如果数组的长度小于20的时候,插入排序算法会比快速排序的算法快。经统计,这样会带来15%的提速。
代码:
public class quickSort {
public void sort(int[] nums) {
if(nums.length == 0 || nums.length == 1) {
return ;
}
quickSort(nums,0,nums.length -1,10);
}
private void quickSort(int[] nums,int left,int right,int offset) {
if(left + offset <=right) {
//找到枢纽元素
int pivot = getPivot(nums,left,right);
int i = left ;
//最后一个元素肯定比枢纽大
int j = right -1;
for(;;) {
//左边要找到比枢纽值大的位置就停止
while(nums[++i]<=pivot) {}
//右边要找到比枢纽值小的位置就停止
while(nums[--j]>=pivot) {}
//只有i>=j的时候才进行交换
if(i<j) {
swap(nums,i,j);
}
else {
break;
}
}
//最后一定要对pivot的位置进行更新,因为前面可能还有比它大的元素
swap(nums,right-1,i);
quickSort(nums,left,i-1,10);
quickSort(nums,i+1,right,10);
}
else {
insertSort(nums,left,right);
}
}
//插入排序
private void insertSort(int[] nums,int left,int right) {
int j= 0;
for(int i = left+1;i<=right;i++) {
int temp = nums[i];
for( j= i;j>left&&temp<nums[j-1];j--) {
nums[j] = nums[j-1];
}
nums[j] = temp;
}
}
//对前中后三个元素进行排序并返回枢纽元素
private int getPivot(int nums[],int left,int right) {
int center = (left+right)/2;
//这个过后,left的位置元素小于center位置的
if(nums[left] > nums[center]) {
swap(nums,left,center);
}
//这个过后,center位置的元素小于right位置的
if(nums[center]>nums[right]) {
swap(nums,center,right);
}
//这个过后,left位置小于center位置
if(nums[left] > nums[center]) {
swap(nums,left,center);
}
//交换center和right-1位置的元素
swap(nums,center,right-1);
//返回枢纽的值
return nums[right-1];
}
//交换两个元素的值
private void swap(int[] nums,int a,int b) {
int temp = 0;
temp = nums[a];
nums[a] = nums[b];
nums[b] = temp;
}
public static void main(String[] args) {
quickSort sort = new quickSort();
int[] nums = {5,9,7,6,88,45,62,1,7,777,4,84,12,58,64,73,29,15};
sort.sort(nums);
for(int i = 0;i<nums.length;i++) {
System.out.print(nums[i]+",");
}
}
}