快速排序

快速排序

快排的思路是:选定最左边的数为参考值,从第二个数开始与参考值进行比较,小的集中在参考值的左边,大的不用管

package cn.stu.test;

import java.util.Arrays;

public class SortQuick {

    public static void main(String[] args) {
        int[] arr1 = {12,34,62,23,14,52,36,78,96};
        int[] sort = sortQuick(arr1,0,arr1.length-1);
        System.out.println(Arrays.toString(sort));
    }

    public static int[] sortQuick(int[] arr,int left,int right) {
        if(right<left)
            return arr;

        int i = sort(arr,left,right);
        sortQuick(arr, left, i-1);
        sortQuick(arr, i+1, right);
        return arr;
    }

    public static int sort(int[] arr, int left, int right){
        int temp = arr[left];
        int j =left;
        for(int i=left+1;i<=right;i++){
            if(arr[i]<temp){
                swap(arr, i, j+1);
                j++;
            }
        }
        swap(arr, left, j);

        return j;
    }

    public static void swap(int[] arr, int i, int j){
        int temp = arr[j];
        arr[j] = arr[i];
        arr[i] = temp;
    }
}

问题一:简单优化?
优化一:递归到个数比较少的时候,可以采用插入排序

    //增加插入函数
public static void sort_insert(int[] arr, int left, int right) {
    for (int i = left + 1; i <= right; i++) {
         int j = i;
         while ((j >= left + 1) && (arr[j] < arr[j - 1])) {
             swap(arr, j-1, j);
             j--;
         }
    }
}

    //修改快排递归的退出条件
public static int[] sortQuick(int[] arr,int left,int right) {
    if(right-left <=4){
        sort_insert(arr, left, right);
        return arr;
    }

    int i = sort(arr,left,right);
    sortQuick(arr, left, i-1);
    sortQuick(arr, i+1, right);
    return arr;
}

问题二:近乎有序的数组使用快排的时候,运行效率退化成了O(n²),怎么优化?
优化二:不要固定使用左边第一个元素作为参考值,可以通过一个随机数,选出数组一个随机元素,再将这个随机元素与左边第一个元素交换位置。

public static int[] sortQuick(int[] arr,int left,int right) {
    if(right-left <=4){
        sort_insert(arr, left, right);
        return arr;
    }
    /*产生随机的参考元素*/
    Random r = new Random(System.nanoTime());
    int num = r.nextInt(1000)%(right-left)+left;
    swap(arr, left, num);

    int i = sort(arr,left,right);
    sortQuick(arr, left, i-1);
    sortQuick(arr, i+1, right);
    return arr;
}

问题三:当数组里面有大量的重复数据的时候,快排又O(n²)了
优化三:可以采用双路快排的方式,将重复的数据分散到两个子数组,提高平衡

方式一:是前面的快排的拓展,前面是从一边入手来寻找参考点的位置,这里是从两边入手,两个索引不同的时候就交换元素,两个索引相同的时候代表找到合适的位置。
    public static int sortTwoWay1(int[] arr, int left, int right) {
        int i = left;
        int j = right;
        int temp = arr[i];
        i = i+1;
        while(j>=i){
            while(j>=i&&arr[j]>temp){
                j--;
            }
            while(j>=i&&arr[i]<temp){
                i++;
            }
            if(j>i){
                swap(arr, i, j);
                j--;
                i++;
            }
        }
        swap(arr, left, j);
        return j;
    }

方式二:跟方法一不大一样的地方就是判断的时候,这里采用直接交换的方式,免去判断的操作,不过这样反而增加了一些额外的交换操作,可能消耗一定的时间。
    public static int sort_2(int[] arr, int left, int right) {
        int i = left;
        int j = right;
        int temp = arr[i];
        while(j>i){
            while(j>i&&arr[j]>temp){
                j--;
            }
            arr[i] = arr[j];
            while(j>i&&arr[i]<temp){
                i++;
            }
            arr[j] = arr[i];
        }
        arr[j] = temp;
        return j;
    }

还是问题三?
优化四:采用三路快排的方式,将大量重复的元素集中在一起,迭代循环的时候就直接跳过这部分的处理,从而加快处理速度。
因为要返回两个索引,所以直接写进快排的函数里面,不再封装。

public static int[] sortQuick(int[] arr,int left,int right) {
        if(right < left){
            return arr;
        }

        int i = left;
        int temp = arr[left];
        int j = right+1;
        int k = i+1;
        while(k<j){
            if(arr[k]>temp){
                swap(arr,k,j-1);
                j--;
            }else if(arr[k]<temp){
                swap(arr,k,i+1);
                i++;
                k++;
            }else{
                k++;
            }
        }
        swap(arr,i,left);
        sortQuick(arr, left, i-1);
        sortQuick(arr, j, right);
        return arr;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值