排序算法(2)快排

交换排序

思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

一、冒泡排序

java 复制代码
 public static void BubbleSort(int[] array){
        boolean flg = false;
        for(int i = 0;i<array.length-1;i++){
            for (int j = 0; j < array.length-1-i; j++) {
                if(array[j]>array[j+1]){
                    swap(array,j,j+1);
                    flg = true;
                }
            }
            if(!flg){
                break;
            }
        }
    }

总结

  1. 冒泡排序是一种非常容易理解的排序

  2. 时间复杂度:O(N^2)

  3. 空间复杂度:O(1)

  4. 稳定性:稳定

二、快速排序

基本思想:基于分治法的算法。任取待排序元素序列中的某元 素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

1、Hoare法
java 复制代码
//快排,递归,Hoare法
    //时间复杂度最坏下O(N^2)
    //快排一般说均匀分割下复杂度优化后趋于n*logn
    public static void quickSort(int[] array){
        quick(array,0,array.length-1);
    }
    private static void quick(int[] array,int start,int end) {
        if(start>=end){
            return;
        }
        int par = partition(array,start,end);
        quick(array,start,par-1);
        quick(array,par+1,end);
    }
    private static int partition(int[] array,int left,int right){
        int i = left;
        int pivot = array[left];
        while(left<right){
            while(left<right && array[right]>=pivot){
                right--;
            }
            while(left<right && array[left]<=pivot){
                left++;
            }
            swap(array,left,right);
        }
        swap(array,left,i);

        return left;
    }

时间复杂度:最坏情况下O(N^2),如果是均匀分割下复杂度优化后趋于n*logn,快排一般用于乱序

问题:

1、堆和快排都是n*logn,那么那个更快呢?

快排还是更快一些,因为时间复杂度是粗略估计的,实际上堆排是kn*logn,最后都把k给去掉了,快排有可能是2logn,堆排是3logn。

2、与基准值比较的时候可以不写'='吗?

不可以!!!

3、为什么从右边开始?

如果先走左边导致最后相遇的地方是比基准大的数据,交换完后,会把大的放到了前面,不满足快排思想。

注意:因为快排是递归,数据太多的情况下会导致溢出

2、挖坑法
java 复制代码
 private static int partition2(int[] array,int left,int right){
       int tmp = array[left];
       while(left<right){
           while(left<right&&array[right]>=tmp){
               right--;
           }
           array[left] = array[right];
           while(left<right&&array[left]<=tmp){
               left++;
           }
           array[right] = array[left];
       }
       array[left] = tmp;
       return left;
    }
3、双指针法(了解)
java 复制代码
 private static int partition3(int[] array, int left, int right) {
        int prev = left ;
        int cur = left+1;
        while (cur <= right) {
            if(array[cur] < array[left] && array[++prev] != array[cur]) {
                 swap(array,cur,prev);
            }
                cur++;
        }
        swap(array,prev,left);
        return prev;
    }
4、快速排序优化(减小递归次数)
  1. 三数取中法选key

  2. 递归到小的子区间时,可以考虑使用插入排序

趋于有序的情况下,插入排序效率最高,并且可以减少递归次数

java 复制代码
   private static void quick(int[] array,int start,int end) {
        if(start>=end){
            return;
        }
        if(end-start+1<=5){
            insertSortRange(array,start,end);
            return;
        }
        
        int index = midTreeNum(array,start,end);
        swap(array,index,start);
        
        int par = partition(array,start,end);
        quick(array,start,par-1);
        quick(array,par+1,end);
    }
    public static void insertSortRange(int[] array,int start,int end){
        for(int i = start+1;i<=end;i++){
            int tmp = array[i];
            int j=i-1;
            for(;j>=start;j--){
                if(array[j]>tmp){
                    array[j+1] = array[j];
                }else{
                    break;
                }
            }
            array[j+1] = tmp;
        }
    }
    private static int partition(int[] array,int left,int right){
        int i = left;
        int tmp = array[left];
        while(left<right){
            while(left<right && array[right]>=tmp){
                right--;
            }
            while(left<right && array[left]<=tmp){
                left++;
            }
            swap(array,left,right);
        }
        swap(array,left,i);

        return left;
    }
    private static int midTreeNum(int[] array,int left,int right){
        int mid = (left+right)/2;
        //返回的是中位数的下标
        if(array[left]<array[right]){
            if (array[mid]<array[left]){
                return left;
            }else if(array[mid]>array[right]){
                return right;
            }else{
                return mid;
            }
        }else{
            if (array[mid]>array[left]){
                return left;
            }else if(array[mid]<array[right]){
                return right;
            }else{
                return mid;
            }
        }
    }
5、快排非递归

使用栈

java 复制代码
    void quickSortNonR(int[] array) {
        Stack<Integer> stack = new Stack<>();
        int left = 0;
        int right = array.length-1;
        int par = partition(array,left,right);
        if(par>left+1){
            //如果左数只有一个值则不需要排序
            //先放左再放右
            stack.push(left);
            stack.push(par-1);
        }
        if(par<right-1){
            //先放左再放右
            stack.push(par+1);
            stack.push(right);
        }
        while(!stack.isEmpty()){
            //先取出来的是右之后是左
            right = stack.pop();
            left = stack.pop();
            par = partition(array,left,right);
            //重复最初的操作
            if(par>left+1){
                //如果左数只有一个值则不需要排序
                stack.push(left);
                stack.push(par-1);
            }
            if(par<right-1){
                stack.push(par+1);
                stack.push(right);
            }
        }
    }

总结:

  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

2、时间复杂度:O(N*logN)

3、空间复杂度:O(logN)(递归开空间)

4、不稳定

例题:设一组初始记录关键字序列为(65,56,72,99,86,25,34,66),则以第一个关键字65为基准而得到的一趟快速排序结果是()

A: 34,56,25,65,86,99,72,66 B: 25,34,56,65,99,86,72,66

C: 34,56,25,65,66,99,86,72 D: 34,56,25,65,99,86,72,66

优先尝试挖坑法,之后是Hoare法,最后双指针(很少用)

相关推荐
喵叔哟几秒前
重构代码中引入外部方法和引入本地扩展的区别
java·开发语言·重构
尘浮生7 分钟前
Java项目实战II基于微信小程序的电影院买票选座系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
不是二师兄的八戒30 分钟前
本地 PHP 和 Java 开发环境 Docker 化与配置开机自启
java·docker·php
爱编程的小生42 分钟前
Easyexcel(2-文件读取)
java·excel
带多刺的玫瑰1 小时前
Leecode刷题C语言之统计不是特殊数字的数字数量
java·c语言·算法
爱敲代码的憨仔1 小时前
《线性代数的本质》
线性代数·算法·决策树
yigan_Eins1 小时前
【数论】莫比乌斯函数及其反演
c++·经验分享·算法
阿史大杯茶1 小时前
AtCoder Beginner Contest 381(ABCDEF 题)视频讲解
数据结构·c++·算法
计算机毕设指导62 小时前
基于 SpringBoot 的作业管理系统【附源码】
java·vue.js·spring boot·后端·mysql·spring·intellij-idea
Gu Gu Study2 小时前
枚举与lambda表达式,枚举实现单例模式为什么是安全的,lambda表达式与函数式接口的小九九~
java·开发语言