JavaDS--快排的四种方法

快速排序

原理:
  1. 从区间中取一个数据做基准值,按照该基准值将区间分割左右两部分
    左部分 基准值 右部分
  2. 按照快排的思想排左半部分
  3. 按照快排的思想排右半部分
    时间复杂度:O(N)
    最优情况下:O(NlogN)
    场景:每次都均分
    最差情况下:O(N^2),每次划分都拿到极值
    场景:数据有序或者接近有序
    应用场景:数据量大,比较随机的(数据杂乱)
    数据量大:将来递归胜读可能比较深–>每次递归都需要在栈中压入一个栈帧
    栈帧:函数在运行期间要保存的中间结果–>比如:函数中的局部变量、参数、返回值信息
    栈是有大小的–>可能导致栈溢出
    public static void quickSort(int[] arr,int left ,int right){
//        if(right-left<16){
//            数据量小的时候可以调用别的排序方法,避免栈溢出,
//            降低栈溢出的概率,但是不能杜绝
//            如果想要绝对的杜绝递归过深导致栈溢出的情况:
//            不要使用递归,用循环
			  递归转循环,有些时候可以直接转,有些情况不能直接转,需要借助栈(二叉树前、中、后序非递归遍历)
			  为什么将递归递归转化为循环需要借助栈的数据结构?
			  栈的特性:后进先出
			  递归:先调用的后退出,后调用的先退出
			  
//        }
//        else
        if(right-left > 1){
            //说明区间中至少有两个元素
            //按照基准值对【left,right)
            int dir=partion(arr,left,right);
            //递归排基准值的左半侧
            quickSort(arr,left,dir);
            //递归排基准值的右半侧
            quickSort(arr,dir+1,right);
        }
    }

标准值优化:使用三数取中发对每次取到的最值防止出现极端情况,加大出现均分的概率,拿到极值的概率就降低了,认为:快排看的是平均复杂度O(NlogN)

    public static int getIndex(int[] arr,int left,int right ){
        int mid=left +((right-left)>>1);
        if(arr[left]<arr[right-1]){
            if(arr[mid]<arr[left]){
                return left;
            }else if(arr[mid]>arr[right-1]){
                return right-1;
            }else {
                return mid;
            }
        }
        else{
            if(arr[mid]>arr[left]){
                return left;
            }else if(arr[mid]<arr[right-1]){
                return right-1;
            }else{
                return mid;
            }
        }

    }

方式一:Hoare
1. 让begin从前往后找,找比基准值大的元素,找到之后停止
2. 让end从后往前找,找比基准值小的元素,找到之后停止
3. 将begin和end标记的元素进行交换
4. 将基准值与begin位置的数据进行交换

    public static int partion(int[] arr,int left,int right){
        int begin=left;
        int end=right-1;
        int key=arr[end];
        while (begin<end){
            //1.begin从前往后找,找比基准值大的元素
            while (begin<end && arr[begin]<=key){
                begin++;
            }
            //2.end从后往前,找比基准值小的元素
            while (begin<end && arr[end]>=key){
                end--;
            }
            if(begin<end){
                swap(arr,begin,end);
            }
        }
        if (begin!=right-1){
            swap(arr,begin,right-1);
        }
        return begin;
    }

方式二:挖坑法
基本思路和Hoare 法一致,只是不再进行交换,而是进行赋值(填坑+挖坑)

    public static int partion2(int[] arr,int left,int right){
        int begin=left;
        int end =right-1;
        int mid=getIndex(arr,left,right);
        swap(arr,mid,right-1);
        int key=arr[end];
        while (begin<end) {
            //1.begin从前往后找,找比基准值大的元素
            while (begin < end && arr[begin] <= key) {
                begin++;
            }
            //找到了一个比基准值大的元素,用该元素填end位置的元素
            if(begin<end){
                arr[end--]=arr[begin];
            }
            //2.end从后往前,找比基准值小的元素
            while (begin<end && arr[end]>=key){
                end--;
            }
            //end从后往前找到了一个比基准值小的元素,用该元素填begin 位置的坑
            if(begin<end){
                arr[begin++]=arr[end];
            }
        }
        //用Key填最后一个坑
        arr[begin]=key;
        return begin;
    }

方法三:前后索引法一前一后

  • 刚开始时:prev和cur是一前一后的关系
  • 一段时间后:prev和cur之间有距离
    prev+1,cur之间的元素都比基准值大
    public static int partion3(int[] arr,int left,int right){
        int cur=left;
        int prev=cur-1;
        int mid=getIndex(arr,left,right);
        swap(arr,mid,right-1);
        int key=arr[right-1];
        while(cur<right){
            if(arr[cur]<key && ++prev != cur){
                swap(arr,cur,prev);
            }
            ++cur;
        }
        if(++prev!=right-1){
            swap(arr,prev,right-1);
        }
        return  prev;
    }

循环方式:
数据量小的时候可以调用别的排序方法,避免栈溢出,
降低栈溢出的概率,但是不能杜绝
如果想要绝对的杜绝递归过深导致栈溢出的情况:
不要使用递归,用循环
递归转循环,有些时候可以直接转,有些情况不能直接转,需要借助栈(二叉树前、中、后序非递归遍历)
为什么将递归递归转化为循环需要借助栈的数据结构?
栈的特性:后进先出
递归:先调用的后退出,后调用的先退出

    public static int partion(int[] arr,int left,int right){
        int begin=left;
        int end=right-1;
        int key=arr[end];
        while (begin<end){
            //1.begin从前往后找,找比基准值大的元素
                while (begin<end && arr[begin]<=key){
                begin++;
            }
            //2.end从后往前,找比基准值小的元素
            while (begin<end && arr[end]>=key){
                end--;
            }
            if(begin<end){
                swap(arr,begin,end);
            }
        }
        if (begin!=right-1){
            swap(arr,begin,right-1);
        }
        return begin;
    }
        public static void swap(int[] arr,int left,int right){
        int tmp=arr[left];
        arr[left]=arr[right];
        arr[right]=tmp;
    }

    void quickSort(int[] arr){
        Stack<Integer> s=new Stack<>();
        s.push(arr.length);
        s.push(0);
        while (!s.empty()){
            int left=s.pop();
            int right=s.pop();
            if(right-left>1){
                int dir=partion(arr,left,right);
                s.push(right);
                s.push(dir+1);
                s.push(dir);
                s.push(left);
            }
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值