排序思想
快速排序是交换排序的一种,在平均情况下的排序时间复杂度为O(nlogn),在最坏的情况下(数组已经有序的情况下),排序时间复杂度为O(n^2)
快排的思想是每趟排序确定一个基准值的位置,并使得该值左边的值都小于它,右边的值都大于它,再对左右两个子数组进行快排的递归调用,直至所有数字都放到了对应的位置上,排序结束。
所以快排中最重要的操作就是partition()方法的实现,(确定基准值的位置并使得该值左边的值都小于它,右边的值都大于它)
最常见的partition方法就是挖坑法
- 将待排序数组最左边的元素定义为基准值pivot,并设置两个指针(哨兵)low,high分别指向数组的最左边的元素与最右边的元素;
- 此时将pivot从数组中取出,nums[low]这个位置便空了出来,形成了一个坑,等待数据放入;
- 首先移动指针high,使其向左边移动(high–),每次移动之前先检查其指向的元素是否小于pivot基准值,如果不小于则移动,如果小于则将其放入指针low指向的坑里,此时指针high指向的位置空了出来,形成了一个新的坑,等待数据的插入;(仔细思考,这样做的目的就是为了让比pivot小的元素都放到pivot最终位置的左边);
- 接下来移动指针low,使其向右边移动(low++),与上述步骤相似,每次移动之前检查其指向的元素是否大于pivot,如果不大于则移动指针,如果大于则将其放入指针high指向的坑里,此时指针low指向的位置就又空了出来,形成一个坑;(同理,这个操作是为了将比pivot大的元素放到pivot最终位置的右边)
- 重复上述3、4步骤,直到指针low、high相遇
- 相遇之后指针low、high都指向同一个元素,并且该元素中一定没有存放数据,是一个坑,这个位置就是基准值pivot排序之后的最终位置,将pivot放入到这个坑里即可
这样,便完成了一次的partition操作,接下来只需对左右两个子数组递归调用快排即可
代码实现
在代码中我实现了两种不同的partition()方法,第一种是挖坑法,第二种有兴趣的话大家可以看一看,其实只是调整了交换元素操作的位置
public void quickSort(int[] nums, int low, int high){
if(low < high){
int pivot = partition(nums, low, high); //调用partition获得pivot最终的位置,
//并使得pivot左边的值都小于它,右边的值都大于它
quickSort(nums, low, pivot-1); //递归左边的子数组
quickSort(nums, pivot+1, high); //右边的子数组
}
}
//挖坑法
public int partition(int[] nums, int low, int high){
int pivot = nums[low];
while(low<high){
while(low<high && nums[high]>=pivot)
high--;
nums[low] = nums[high];
while(low<high && nums[low]<=pivot)
low++;
nums[high] = nums[low];
}
nums[low] = pivot;
return low;
}
public int partition1(int[] nums, int low, int high){
int k = low;
int pivot = nums[low];
while(low<high){
while(low<high && nums[high]>=pivot)
high--;
while(low<high && nums[low]<=pivot)
low++;
swap(nums,low,high);
}
swap(nums,k,low);
return low;
}
public void swap(int[] nums, int i, int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}