快速排序Quick Sort(其一)
基本思想:
选择一个元素,以这个元素为基点,想办法把这个元素挪到排好序应该所处的地方,它之前的元素都是小于它的,它之后的元素都是大于它的。
对小于这个元素的子数组和大于这个元素的子数组分别继续使用快速排序的思路逐渐递归下去完成整个排序过程。
快速排序的核心:
如何把一个选定的元素挪到正确的位置上,可称为Partition过程,通常使用数组中第一个元素。
Partition具体过程:
设置四个索引:l,r,i,j,其中l,r分别指向数组的前闭后闭,i从l+1开始遍历直到i>r结束,i指向当前考虑的元素位置,j指向小于arr[l]的最后一个位置。其中:arr[l+1,…,j]<arr[l],arr[j+1,…,i-1]>arr[l]。
当arr[i]>arr[l]时,直接i++,当arr[i]<arr[l]时,将arr[j+1]与arr[i]进行交换,同时j++,i++继续考察下一个元素,直到最后将arr[l]与arr[j]的元素进行交换,此时数组像我们想的那样被分为小于arr[l],大于arr[l]的两部分,arr[l]被挪到了它正确的位置,此时指向arr[l]的位置的索引就是j。
public void quickSort(E[] arr,int n){
quickSort(arr,0,n-1);
}
/**
* 对arr[l,...,r]部分进行快速排序
* @param arr
* @param l
* @param r
*/
private void quickSort(E[] arr,int l,int r){
if(l>=r){
//递归到底的情况
return;
}
int p = partition(arr,l,r);
//递归调用
quickSort(arr,l,p-1);
quickSort(arr,p+1,r);
}
/**
* 对arr[l,...,r]部分进行partition操作
* 返回p,使得arr[l,...,p-1]<arr[p];arr[p+1,...,r]>arr[p]
* @param arr
* @param l
* @param r
* @return
*/
private int partition(E[] arr,int l,int r){
E v = arr[l];
//arr[l+1,...,j]<v;arr[j+1,...,i)>v
int j = l;
//此时arr[l+1,...,j(l)]这个区间为空
//arr[j+1,...,i(l+1))这个区间也为空
for(int i=l+1; i<=r;i++){
//当arr[i]>v时,i++就可以,当arr[i]<v时,使arr[j+1]与arr[i]进行交换,直到i>=r
if(arr[i].compareTo(v) <0){
//交换
E temp = arr[j+1];
arr[j+1] = arr[i];
arr[i] = temp;
//交换了一个小于v的值,j++
j++;
}
}
//最后将arr[l] 与arr[j]进行交换
E e = arr[l];
arr[l] = arr[j];
arr[j] = e;
return j;
}
存在的问题:
对于近乎有序的数组进行排序,普通的快速排序生成的递归树不能保证就是logN(与归并排序不同),快速排序最差的情况就退化为O(n^2),整颗递归树的高度为N,可以看作是一个链表,在每一棵树里又用O(N)的时间复杂度,快速排序退化为时间复杂度为O(N*N)。
解决办法:
随机选择一个元素作为标定元素,此时快速排序的时间复杂度为O(NlogN)
/**
* 对arr[l,...,r]部分进行partition操作
* 返回p,使得arr[l,...,p-1]<arr[p];arr[p+1,...,r]>arr[p]
* @param arr
* @param l
* @param r
* @return
*/
private int partition(E[] arr,int l,int r){
//随机化l
int index = (Math.abs(new Random().nextInt())%(r-l+1))+l;
E t = arr[index];
arr[index] = arr[l];
arr[l]= t;
E v = arr[l];
//arr[l+1,...,j]<v;arr[j+1,...,i)>v
int j = l;
//此时arr[l+1,...,j(l)]这个区间为空
//arr[j+1,...,i(l+1))这个区间也为空
for(int i=l+1; i<=r;i++){
//当arr[i]>v时,i++就可以,当arr[i]<v时,使arr[j+1]与arr[i]进行交换,直到i>=r
if(arr[i].compareTo(v) <0){
//交换
E temp = arr[j+1];
arr[j+1] = arr[i];
arr[i] = temp;
//交换了一个小于v的值,j++
j++;
}
}
//最后将arr[l] 与arr[j]进行交换
E e = arr[l];
arr[l] = arr[j];
arr[j] = e;
return j;
}