目录
- 快排时间复杂度和稳定性
- 快速排序(单pivot,pivot总是数组的第一个元素)
- 快速排序(单pivot,pivot总是数组的最后一个元素)
- 快速排序(单pivot,pivot是数组的随机元素)
- 快速排序(双pivot,pivot总是数组的第一个元素)(未完成)
- 快速排序(单pivot,非递归)(未完成)
参考:http://blog.csdn.net/qianqin_2014/article/details/51207165
正文
快排时间复杂度、空间复杂度和稳定性
- 时间最好O (nlogn),最坏O (n^2)
- 空间复杂度取决于递归造成的栈空间的使用,最好情况,递归树的深度为log2n,其空间复杂度也就为O(logn),最坏情况,需要进行n‐1递归调用,其空间复杂度为O(n),平均情况,空间复杂度也为O(logn)
- 不稳定. 比如序列为5 3 3 4 3 8 9 10 11,现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱,不稳定发生在中枢元素和a[j] 交换的时刻
package my;
import java.util.Arrays;
/**
*
* @author sxcai188
*/
public class Main {
/**
* pivot最左
* @param arr
* @param l
* @param r
*/
static void quickSort_1(int[] arr, int l, int r) {
if (l < r) {
int i = l;
int j = r;
int pivot = arr[l];
while (i < j) {
while (i < j && arr[j] >= pivot) {
j--;
}
if (i < j) {
arr[i]=arr[j];
i++;
}
while (i < j && arr[i] < pivot) {
i++;
}
if (i < j) {
arr[j] = arr[i];
j--;
}
}
arr[i] = pivot; //此时i == j,这个位置就是分界线,左边的都小于pivot,右边的都大于pivot
print(arr);
quickSort_1(arr, l, i-1);
quickSort_1(arr, i+1, r);
}
}
/**
* pivot是数组最右的元素
* @param arr
* @param l
* @param r
*/
static void quickSort_2(int[] arr, int l, int r) {
if (l < r) {
int i = l;
int j = r;
int pivot = arr[r];
while (i < j) {
while (i < j && arr[i] <= pivot) {
i++;
}
if (i < j) { //在pivot的左边找到的第一个比pivot大的数
System.out.println(String.format("从i=%d到j=%d,从左到右, 找第一个 > pivot的元素:%d,移动到最右边",i,j,arr[i]));
arr[j]=arr[i];
arr[i]=999;
j--; //现在arr[i]是pivot,所以j向左移动一格。
print(Arrays.copyOfRange(arr, l, r+1));
}
while (i < j && arr[j] > pivot) {
//在pivot右边比pivot大,不做任何事
j--;//从右到左遍历
}
if (i < j) { //在pivot的右边找到的第一个比pivot小或等于的数
System.out.println(String.format("从i=%d到j=%d,从右到左, 找第一个 <= pivot的元素:%d,移动到最左边",i,j, arr[j]));
arr[i] = arr[j];//交换pivot和刚刚找到的pivot的右边找到的第一个比pivot小或等于的数
arr[j] = 999;
i++;//现在arr[j]是pivot,所以i向右移动一格。
print(Arrays.copyOfRange(arr, l, r+1));
}
}
arr[i] = pivot; //i == j处
print(Arrays.copyOfRange(arr, l, r+1));
System.out.println("");
quickSort_2(arr, l, i-1);
quickSort_2(arr, i+1, r);
}
}
/**
* 随机取pivot
* @param arr
* @param l
* @param r
*/
static void quickSort_3(int[] arr, int l, int r) {
if (l < r) {
int i = l;
int j = r;
java.util.Random random=new java.util.Random();
int iRandom = random.nextInt(arr.length);
int pivot = arr[iRandom];
System.out.println(iRandom + ",pivot元素是" + pivot);
//交换
int tmp = arr[iRandom];
arr[iRandom] = arr[r];
arr[r] = tmp;
while (i < j) {
while (i < j && arr[i] <= pivot) {
i++;
}
if (i < j) { //在pivot的左边找到的第一个比pivot大的数
System.out.println(String.format("从i=%d到j=%d,从左到右, 找第一个 > pivot的元素:%d,移动到最右边",i,j,arr[i]));
arr[j]=arr[i];
arr[i]=999;
j--; //现在arr[i]是pivot,所以j向左移动一格。
print(Arrays.copyOfRange(arr, l, r+1));
}
while (i < j && arr[j] > pivot) {
//在pivot右边比pivot大,不做任何事
j--;//从右到左遍历
}
if (i < j) { //在pivot的右边找到的第一个比pivot小或等于的数
System.out.println(String.format("从i=%d到j=%d,从右到左, 找第一个 <= pivot的元素:%d,移动到最左边",i,j, arr[j]));
arr[i] = arr[j];//交换pivot和刚刚找到的pivot的右边找到的第一个比pivot小或等于的数
arr[j] = 999;
i++;//现在arr[j]是pivot,所以i向右移动一格。
print(Arrays.copyOfRange(arr, l, r+1));
}
}
arr[i] = pivot; //i == j处
print(Arrays.copyOfRange(arr, l, r+1));
System.out.println("");
quickSort_2(arr, l, i-1);
quickSort_2(arr, i+1, r);
}
}
/**
* 浅拷贝
* @param arr
*/
private static void print(int[] arr) {
for (int i : arr) {
System.out.print(String.format("%3d,", i));
}
System.out.println("");
}
public static void main(String[] args) {
int[] sample = {1,3,2,4,11,12,5,10,6,7,18,9};
print(sample);
System.out.println("-------start");
quickSort_3(sample, 0, sample.length-1);
System.out.println("-------all finished");
print(sample);
}
}
快速排序在序列中元素很少时,效率将比较低,不然插入排序,因此一般在序列中元素很少时使用插入排序,这样可以提高整体效率。
测试时间随n的变化
附录
- 快排的软肋O(n^2)是插排的强项O(n):基本有序数据,jdk1.8中java.util.Arrays sort() 中,legacy的阈值是数组长度为7,大于阈值用快排,小于阈值用插排
- 插排:斗地主整理手牌,未排序元素插入到已经排序的数组中
- 选择:矮子里选将军,未排序元素和剩下的其他未排序元素比,和最小的元素交换位置
- 冒泡:越大的元素会经由交换慢慢“浮”到数列的顶端,从第一个元素开始两辆比较,反序交换,直至最后一个元素
//插入排序改进:降低swap次数
public static void my_insertionSort(int[] arr, int l, int r) {
if (arr==null) {
throw new Exception("null arr");
}
if (r-l <= 1) {
return;
}
for (int i = l+1; i <= r; i++) {
int j = i;
int t = arr[i];
while (j>l && (arr[j-1] > t)) {
arr[j] = arr[j-1];
j--;
}
if(j<i) {
arr[j] = t;
}
}
}
//选择
private static void selectSort(int arr[], int l, int r) {
if (arr==null) {
throw new Exception("null arr");
}
if (r-l <= 1) {
return;
}
for (int i = l; i<r; i++) {
int min = i;
for (int j = i+1; j<=r ; j++) {
if (arr[j] < arr[min]) {
min = j;
}
}
if (min > i) {
swap(arr, min, i);
}
}
}
//冒泡, 左边是水面,右边是湖底。从湖底冒泡到水面
private static void bubbleSort(int[] arr, int l, int r) {
if (arr==null) {
throw new Exception("null arr");
}
if (r-l <= 1) {
return;
}
for (int i=l; i<=r; i++) {
for (int j=r; j>i; j--) {
if (arr[j] < arr[j-1]) { //正序<,逆序>
swap(arr, j, j-1);
}
}
}
}