1.简介
快速排序是很经典的分治算法,大概的算法思路就是将大数组划分partition为两个子数组,左数组整块小于右数组(max(left)<min(right)),然后递归处理左子数组和右子数组,如下图所示:
2.算法实现
2.1 quickSort递归
挑一个数当作pivot基准, 把数组分割partition成左右两堆, 使左半小于pivot,右半大于pivot,基准数字置于中间。 Conquer 阶段:左右两堆资料各自从事Quicksort 。 Combine 阶段:不做任何事。 |
3.2 划分partition
1.两个指针分别迭代,i for [left,right-1], [left,i]部分永远是划分好的两部分 [left,small]块小于pivot,[small,i]不小于pivot 2.当[left,right-1]划分好,将pivot插入到正确的位置(small+1), small++,交换small位置和right,划分完成 不变式 [left,small]任意元素小于pivot (除了最终small是pivot本身) |
public class QuickSort {
public void sort(int[] nums) {
int left = 0, right = nums.length-1;
if (left<right) {
quickSort(nums, left, right);
}
}
//[left, right] 闭区间
public void quickSort(int[] a, int left, int right) {
int index = partition(a, left, right); // 划分
if (left < index -1) {
quickSort(a, left, index-1); // 左半部分
}
if (index < right) {
quickSort(a, index+1, right); // 右半部分
}
}
int partition(int[] a, int left, int right) {
int pindex = rand.nextInt(right - left + 1) + left; // 随机找pivot
swap(a, pindex, right); //交换到最后
int small = left - 1;//small-left+1是小于pivot值的个数,指向当前遍历到的最后一个最小值
for (int i=left;i<right;i++) {
if (a[i] < a[right]) {
small++;
if (i!=small) {
swap(a, i, small);
}
}
}
small ++;
swap(a, small, right);
return small;
}
void swap(int[] nums, int i,int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
public static void main(String[] args) {
// int[] nums = {1,3,2,4,2,5,2};
// int[] nums = {};
int[] nums = {1};
new QuickSort().sort(nums);
System.out.println(Arrays.toString(nums));
}
}
第二种划分方法,从left、right两端向中间靠拢
int partition(int[] a, int left, int right) {
int pivot = a[(left+right)/2]; //基准点 中位数
while (left <= right) {
while (a[left]<pivot) left++; //一直找到左边大于等于基准的值
while (a[right]>pivot) right--; //从右向左 找到右边小于等于基准的值
if (left<=right) {
swap(a, left, right); // 交换位置
left++;
right--;
}
}
return left;
}
拓展:用partition方法找到中位数
3.算法分析
时间复杂度:平均O(nlogn) 最差O(n2)
空间复杂度:O(logn)