快排的基本思想是:
1.先从数列中取出一个数作为基准数。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。
对于快速排序来说,区间内的数的大小顺序越随机效率越好,这样就能将数据划为为两个区域;
这点和选择排序恰恰相反,选择排序在数组有序时排序效率较高,而对于快排来说,数据有序反而回导致每次可能都只能划分出一个区域,导致效率下降到O(n^2);
所以为了避免极端测试用例,我们在每次进行排序之前,先将区间第一个数和区间里随机选择的一个数进行交换,保证效率。
双路快排:将与pivot相等的数平均分配到左右两个区间中
Random random=new Random(System.currentTimeMillis());
public void quickSort(int []nums,int left,int right){
if(left>=right) return;
int randomIndex=left+ random.nextInt(right-left+1);
swap(nums,left,randomIndex);
int i=left,j=right;
int pivot=nums[left];
while(i<j){
while(i<j&&nums[j]>=pivot){
j--;
}
if(i<j){
swap(nums,i++,j);
}
while(i<j&&nums[i]<=pivot){
i++;
}
if(i<j){
swap(nums,i,j--);
}
}
nums[i]=pivot;
quickSort(nums,left,i-1);
quickSort(nums,i+1,right);
}
public void swap(int []nums,int i,int j){
int tmp=nums[i];
nums[i]=nums[j];
nums[j]=tmp;
}
优化:三路快排
将和pivot相等的数挤压到中间,小于pivot的划分到左边,大于pivot的划分到右边;
之后只需要对左区间和右区间再进行快排即可。
三路快排在有大量相同数据时,可以大幅加快排序的速度。
思路:维护三个变量i,j,k; i的左边是左区间,j的右边是右区间,k用来遍历。
我们从pivot的下标+1处开始遍历,划分好区间后,再把pivot和左区间的最后一个元素交换
Random random=new Random(System.currentTimeMillis());
public void quickSort(int []nums,int left,int right){
if(left>=right) return;
int randomIndex=left+ random.nextInt(right-left+1);
swap(nums,left,randomIndex);
int i=left+1,j=right,k=left+1;
int pivot=nums[left];
while(k<=j){
if(nums[k]<pivot){
swap(nums,i++,k++); //i++,k--,因为交换过来的数k之前九遍历过来,所以无需再看
}
else if(nums[k]==pivot){
k++;
}
else{
swap(nums,k,j--); //此处k不++,因为交换过来的数k还没遍历过,所以还要再看
}
}
swap(nums,left,i-1);
quickSort(nums,left,i-2);
quickSort(nums,j+1,right);
}
public void swap(int []nums,int i,int j){
int tmp=nums[i];
nums[i]=nums[j];
nums[j]=tmp;
}