快速排序:
21世纪最重要的算法之一,基于分治思想O(nlogn)
算法思想:从待排序的数组中选取任意一个元素【l...r】,成为分区点(基准值)开始遍历过程,每当发现比基准值小的元素就放在基准值左边,大于等于基准值的元素放在基准值右边,当结束一次遍历时,基准值一定在最终位置。
一:默认以最左边为基准值( 图示 ):
private static int partition(int[] array, int l, int r) {
//默认第一个职位基准值
int val = array[l];
//[l+1-->j]<val
int j = l;
//[j+1-->i-1]>val
int i = l + 1;
for (; i <= r; i++) {
if (array[i] < val) {
swap(array, i, j + 1);
j++;
}
}
//j指向的元素是小于val的,所以交换j和l
swap(array, j, l);
return j;
}
二:当待排序的集合近乎有序时由于默认选择的第一个元素作为基准值,会导致基准值划分的两个子数组严重不均衡,此时分层下来的结果近乎为零,此时快排退化为幅度为O(n2)排序算法。
private static int partition(int[] array, int l, int r) {
//返回下标为l-->r的任意一个数
//int randomIndex = (int) (Math.random() * (r - l + 1) + l);
Random ran = new Random();
int randomIndex = ran.nextInt(r - l + 1) + l;
swap(array, l, randomIndex);
//默认第一个职位基准值
int val = array[l];
//[l+1-->j]<val
int j = l;
//[j+1-->i-1]>val
int i = l + 1;
for (; i <= r; i++) {
if (array[i] < val) {
swap(array, i, j + 1);
j++;
}
}
//j指向的元素是小于val的,所以交换j和l
swap(array, j, l);
return j;
}
三:当待排序集合包含大量重复元素时,由于与基本值相等的元素个数过多,导致数组长度不够均衡,此时分层下来的 结果近乎n层,快拍退化为O(n^2)。
解决:二路快排:将大于和小于val的的元素放在数组的两端,i索引不断向后扫描,当i的元素小于val时, i++,j索引不断向前扫描,当j的元素大于val时j--,当i碰到一个>=val的元素以及当j碰到一个<=val的元素,交换i与j的元素,i++,j--。
private static int partition2(int[] array, int l, int r) {
Random random = new Random();
int rendomIndex = random.nextInt(r - l + 1) + l;
swap(array, l, rendomIndex);
int val = array[l];
//array[l+1-->i-1] < val
int i = l + 1;
//array[j+1-->r] > val
int j = r;
while (true) {
//从前向后扫描
while (i <= r && array[i] < val) i++;
while (j >= l + 1 && array[j] > val) j--;
if (i > j) {
break;
}
swap(array,i,j);
i++;
j--;
}
swap(array,l,j);
return j;
}
四:三路快排(继续优化):
代码实现:
private static void quickSortInternal3(int[] array, int l, int r) {
if (l > r) {
return;
}
int randomIndex = (int) Math.random() * (r - l + 1) + l;
swap(array, randomIndex, l);
int val = array[l];
//[l+1,lt]<val
int lt = l;
//[lt+1,i-1]==val
int i = l + 1;
//[gt,r]>val
int gt = r + 1;
while (i < gt) {
if (array[i] < val) {
swap(array, i, lt + 1);
lt++;
i++;
} else if (array[i] > val) {
swap(array, i, gt - 1);
gt--;
} else {
i++;
}
}
swap(array, lt, l);
quickSortInternal3(array, l, lt - 1);
quickSortInternal3(array, gt, r);
}
五.复杂度分析:
时间复杂度:
最坏:当待排序元素近乎有序时,若选取的基准值恰好为最大值时,此时分层退化成O(n),快排时间复杂度
退化为O(n^2);
最好:每次分区点的选择都恰好在中间位置,O(nlogn)
空间复杂度:
O(1),原地排序
稳定性:不稳定 【eg:5 2 3 1 5,若选取的基准值恰好为最后一个元素5】