快速排序及其变种 Java

目录

  1. 快排时间复杂度和稳定性
  2. 快速排序(单pivot,pivot总是数组的第一个元素)
  3. 快速排序(单pivot,pivot总是数组的最后一个元素)
  4. 快速排序(单pivot,pivot是数组的随机元素)
  5. 快速排序(双pivot,pivot总是数组的第一个元素)(未完成)
  6. 快速排序(单pivot,非递归)(未完成)

参考:http://blog.csdn.net/qianqin_2014/article/details/51207165

正文
这里写图片描述

快排时间复杂度、空间复杂度和稳定性

  1. 时间最好O (nlogn),最坏O (n^2)
  2. 空间复杂度取决于递归造成的栈空间的使用,最好情况,递归树的深度为log2n,其空间复杂度也就为O(logn),最坏情况,需要进行n‐1递归调用,其空间复杂度为O(n),平均情况,空间复杂度也为O(logn)
  3. 不稳定. 比如序列为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);
                }
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值