import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* 快速排序:基于分治思想,将一个数组分成两个数组独立排序,与归并排序是互补的,与归并排序不同的是它不是任意的划分,而是基于某个值的切分,使两边的数组分别不大于
* 和不小于这个值,所以当两边子数组都有序时整个数组也有序了,并不需要归并;其核心方法是切分,切分的过程也就是排序的过程,每次切分后返回选定值的索引。
* 时间复杂度:对于长度为N的任意数组,快速排序平均需要 ~2NlogN 次比较,最多需要 ~1/2N^2 次比较,所以排序之前随机打乱很重要,可以避免最坏情况。
*
* 改进:
* 1. 对于小数组,插入排序比快速排序快,只需要修改一行代码 --> if (hi <= lo + M) { Insertion.sort(toBeSortedArray,lo,hi); return;}
* 2. 三取样切分
* 3. 三向快速排序:针对包含大量重复元素的数组的排序优化
*/
public class QuickSort {
private Comparable[] toBeSortedArray; // 待排序的数组
public QuickSort(Comparable[] toBeSortedArray) {
this.toBeSortedArray = toBeSortedArray;
}
// 判断 toBeSortedArray[i] 是否小于 toBeSortedArray[j]
private boolean less(int i, int j) {
return toBeSortedArray[i].compareTo(toBeSortedArray[j]) < 0;
}
// 交换 toBeSortedArray[i] 和 toBeSortedArray[j]
private void exch(int i, int j) {
Comparable temp = toBeSortedArray[i];
toBeSortedArray[i] = toBeSortedArray[j];
toBeSortedArray[j] = temp;
}
// 判断数组是否有序
public boolean isSorted() {
for (int i = 1; i < toBeSortedArray.length; i++)
if (!less(i-1,i))
return false;
return true;
}
public void sort() {
// 随机打乱数组
List<Comparable> list = Arrays.asList(toBeSortedArray);
Collections.shuffle(list);
list.toArray(toBeSortedArray);
// sort2way(0, toBeSortedArray.length-1);
sort3way(0, toBeSortedArray.length-1);
}
// 普通二向切分
private void sort2way(int lo, int hi) {
if (hi <= lo) return; // 递归返回条件
int j = partition(lo, hi); // 每次递归都会返回一个中间值的位置,必须使用栈内存,所以快速排序的空间复杂度就是栈用的空间 lgN
sort2way(lo, j-1); // 左半部分排序
sort2way(j+1,hi); // 右半部分排序
}
/*
二向切分:选定第一个位置元素[lo]为切分标准,定义两个扫描指针,i从左往右找>=[lo]的,j从右往左找<=[lo]的,找到则交换它们,
当i,j第一次相遇后,交换[lo] 和 [j] (因为j一定指向小于等于[lo]的元素);切分的过程也就是排序的过程,每次切分后将数组切分为
[lo~j-1],[j],[j+1~hi] 最后返回选定值的索引 j。
*/
private int partition(int lo, int hi) {
int i = lo;
int j = hi + 1; // 这里的取值是为了搭配下面的自增自减操作
for (;;) {
while (less(++i,lo)) if (i == hi) break; // 从左往右扫描直到找到大于[lo]的或 到尾
while (less(lo,--j)) if (j == lo) break; // 从右往左扫描直到找到小于[lo]的或 到头
if (i >= j) break; // 第一次相遇退出
exch(i,j); // 交换 [i] 和 [j]
}
exch(lo,j); // 最后交换 [lo] 和 [j]
return j;
}
/*
三向切分:选定一个值v,定义三个扫描指针lt、i、gt,使[lo~lt-1]<v,[lt~i-1]=v,[i~gt]?,[gt+1~hi]>v,一开始 lt=lo, i=lo+1, gt=hi,遍历数组对
元素[i]进行三向比较:
[i] < v, exch(lt++, i++);
[i] > v, exch(gt--, i)
[i] = v, i++
最终:a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi]。
*/
private void sort3way(int lo, int hi) {
if (hi <= lo) return; // 递归返回条件
int lt = lo; int i = lo + 1; int gt = hi; // 初始位置
Comparable v = toBeSortedArray[lo]; // 选定起始值
while (i <= gt) {
int cmp = toBeSortedArray[i].compareTo(v);
if (cmp < 0) exch(lt++, i++);
else if (cmp > 0) exch(i, gt--);
else i++;
}
// 最终:a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi]
sort3way(lo, lt-1);
sort3way(gt+1, hi);
}
}
class Test{
public static void main(String[] args) {
Integer[] testArray = {5,8,4,1,2,3,7,9,6,0};
QuickSort quickSortTD = new QuickSort(testArray);
quickSortTD.sort();
System.out.println(quickSortTD.isSorted());
System.out.println(Arrays.toString(testArray));
}
/*
true
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
*/
}
3-排序之快速排序
最新推荐文章于 2024-07-18 00:08:52 发布