快速排序

快速排序

算法思想:(分治思想)

1.在序列中任意位置选取一个基准点,将比基准点小的值全部放到基准点的左边,比基准点大的值全部放到基准点的右边;每次排序过后 基准点一定在它的最终位置;

2,以基准点为基点 进行左右分割 ,将数组继续分割成小的序列并在小序列中重复过程1,

所以,我们可以选取第一个位置/或者最后一个位置为基准点(这里选取第一个)下标设置成l

基准值为value

tip: 在和归并比较时,优先用快速排序;

随机快排

在这里插入图片描述

    public static void main(String[] args) {
        int[] array=new int[]{2,1,4,3,1,6,5};
        quickSort(array);
        for(int i:array){
            System.out.print(i+" ");
        }
    }

    /**
     * 快速排序
     * @param array
     */
    public static void quickSort(int[] array){
        int n=array.length;
        if(n<=1){
            return ;
        }else{

            quickSortInternal(array,0,n-1);

        }
    }
    public static void quickSortInternal(int[] array,int l,int r){
        if(l>=r){
            return ;
        }else{
            int q=partition(array,l,r);//一次分割后 基准点一定在它的最终位置上
            quickSortInternal(array,l,q-1);
            quickSortInternal(array,q+1,r);

        }


    }
    private static int partition(int[] array,int l,int r){
        int value=array[l];//基准值的选取 这里选取第一个
        //array[l+1……j]<value的区间,刚开始时 此区间没有元素,所以j初始值为l
        int j=l;
        //arra[j+1……i-1]>value的区间,刚开始时也没有元素,但是开始也一定在l+1,在基准值之后
         //因为i以后是没有进行比较的空间
        int i=l+1;
        for(;i<=r;i++){
            if(value>array[i]){
                swap(array,j+1,i);//j是<value的最后一个元素,所以只能和j+1进行交换,小于value的空间扩大一个
                j++;//将j的下标向后移一个
            }
        }
        swap(array,j,l);//此时全部<value 的空间一定在<=j的区间里,
        return j;

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

分析:时间复杂度:快排如果每次都近乎分割成大小相近的空间,那么该算法的时间复杂度就是O(nlogn);

(因为它的空间分割也是递归得来的)

但是上面选取分区点(基准值)的方法有bug,如果待排序数组是一个近乎有序的数组,呢么我们分区的次数就会近乎为n 那么快排就会退化成O(n^2)时间复杂度的算法;

空间复杂度 :O(1),与归并排序相比 没有开辟新空间

稳定性:不问定排序 5 16 4 2 5 和基准点的选取有关,如果 该序列 选取第一个位置作为基准点就是不稳定的 ,最后一个5,

会被换到前面,

优化:

让每次基准点(分区点)的选取都是随机的,就可以避面 左右两端区间严重不均衡 让分层趋向于O(n)导致快排退化成O(n^2)

我们只用改变 partition 方法即可,

private static int partition2(int[] array,int l,int r){
        int randomIndex=(int) (Math.random()*(r-l+1))+l;//生成【l-r】的随机数
        swap(array,l,r);
        int j=l,i=l+1,value=array[l];
        for(;i<=r;i++){
            if(array[i]<value){
                swap(array,j+1,i);
                j++;
            }
        }
        swap(array,j,l);
        return j;

    }

二路快排

首先,上面我们已经对数组基本有序的的情况作出了优化,但是还有一种情况**,如果数组中重复的元素过多导致分组两端数组长度严重不均衡**,数组分割次数依旧会是n, 这时 还是会退化成O(n^2)时间复杂度的算法

将重复元素交换位置来实现左右两个数组都有元素,避免分组不平衡;

所以 我们采用将和基准点相同的元素放到基准点的左右两端

在这里插入图片描述

 private static int partition3(int[] array,int l,int r){
        int randomIndex=(int) (Math.random()*(r-l+1)+l);//产生随机下标
        swap(array,randomIndex,l);
        //array[l+1……i]<value     array[j……r]>value
        int i=l+1,j=r,value=array[l];
        while(true){
            while(array[i]<value&&i<=r)i++;//碰到大于value的值 停下
            while(array[j]>value&&j>=l)j--;//碰到小于value的值停下
            if(i>j){//这时表明所有元素都已经遍历完,退出循环
                break;
            }
            swap(array,i,j);//否则,交换两个元素
          i++;
          j--;
        }
        swap(array,j,l);//j就是<value的最后一个值交换
        return j;

    }

三路快排(大量出现等于基准值的情况),在重复元素比较多的情况

在这里插入图片描述

private static int partition4(int[] array,int l,int h){

        //三路快排
        int value=array[l];
        //array[l+1,lt]<value
        int lt=l;
        //array[gt,r]>value
        int gt=h+1;//从后向前遍历,这时,>value的空间一个都没有,所以是h+1
        //array[lt+1,i-1]==value
        int i=l+1;//从中间元素开始排序
        while(i<gt){//i>=gt,说明所有元素都已经遍历完了
            if(array[i]<value){
                swap(array,lt+1,i);
                lt++;//<value的空间增加一个
                i++;//i++,向后走
            }else if(array[i]>value){
                swap(array,gt-1,i);
                gt--;//>value的空间增加一个
            }else{
                i++;//==value的空间增加一个
            }
        }
        swap(array,l,lt);

        return gt;


    }

优化

我们知道当数据集小的时候,直接插入排序就 变得非常有效率

所以,我们可以让数据集到达非常小的时候进行插入排序

public static void insert(int[] array,int l,int r){
    for(int i=l+1;i<=r;i++){
        int j=i-1,value=array[i];
        while(j>=l&&value<array[j]){
            array[j+1]=array[j];
            j--;
        }
        array[j+1]=value;
    }   
    
}
public static void quickSortInternal(int[] array,int l,int r){
    if(l>=r){
        insert(array,l,r)
        return ;
    }else{
        int q=partition(array,l,r);//一次分割后 基准点一定在它的最终位置上
        quickSortInternal(array,l,q-1);
        quickSortInternal(array,q+1,r);

    }
 }

总结

快速排序是一种很快的算法,但是有些时候,一些数据在快排这反而不如别的排序,这时我们对快排进行了优化,

二路,三路快排,目的是为了解决数据集近乎有序或者数据集重复太多的情况下,因为分割的不平衡导致的分割次数的增加 从而时间复杂度退化的问题:

时间复杂度:O(nlogn)

空间复杂度:O(1)

稳定性:不稳定排序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值