排序

前言

排序算法是一个老生常谈问题,目前主要学习了选择排序、冒泡排序、插入排序、归并排序、随机快排、堆排序、计数排序、基数排序,一共八种排序方式。

名称解释

稳定性: 假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

打印int的二进制

代码

 public  void print(int number){
        for (int i = 31; i >= 0; i--) {
            /**
             * << 向左移动,空位补0,被移除的高位丢企
             * >> 带符号向右移动
             * >>> 不带符号向右移动 空位补0
             * & 按位与 1和1 为1 其他为0
             * | 任何二进制位和0进行0, 任何二进制和1进行为1
             * ^ 按位异或 任何相同二进制位进行^运算,结果是0;不相同二进制位^运算结果是1.
             * ~ 运算符 对位求反 1变0, 0变1
             **/
            System.out.print((number&(1<<i))==0?"0":"1");
        }
    }

选择排序

代码

public void selectSort(int[] numbers){
        if (numbers==null||numbers.length<2){
            return;
        }
       for (int i=0;i<numbers.length;i++){
           int min=i;
           for (int j=i+1;j<numbers.length;j++){
              min= numbers[min]>numbers[j]?j:min;
           }
           exchange(numbers,i,min);
       }

    }

冒泡排序

代码

    public void bubbleSort(int[] numbers){
        if (numbers==null||numbers.length<2){
            return;
        }
        for (int i=numbers.length-1;i>=0;i--){
            for(int j=0;j<i;j++){
                if (numbers[j]>numbers[j+1]){
                    exchange(numbers,j,j+1);
                }
            }
        }
    }

插入排序

代码

public void insertSort1(int[] numbers){
        if (numbers==null||numbers.length<2){
            return;
        }
        for(int i=0;i<numbers.length;i++){
            for (int j=i;j>0&&numbers[j]<numbers[j-1];j--){
                exchange(numbers,j,j-1);
            }
        }
    }

归并排序

代码

  public void sortArrRecursive(int[] arr,int L,int R){
        //如果L==R则不要进行最后的细分
        if (L==R){
            return;
        }
        int mid=(L+R)/2;
        sortArrRecursive(arr,L,mid);
        sortArrRecursive(arr,mid+1,R);
        merge(arr,L,mid,R);
        System.out.println("排序后:"+Arrays.toString(arr));
    }

    /**
     * description 将左右两边进行排序后放回原数组中
     * param [arr, l, mid, r]
     * return void
     * author zhuyang
     * createTime 2021/12/1 22:58
     **/
    private void merge(int[] arr, int l, int mid, int r) {
        int i=0;
        int[] help=new int[r-l+1];
        int q=mid+1;
        int OneL=l;
        while (l<=mid&&q<=r){
            help[i++]=arr[l]<arr[q]?arr[l++]:arr[q++];
        }
        while (l<=mid){
            help[i++]=arr[l++];
        }
        while (q<=r){
            help[i++]=arr[q++];
        }
        for (int i1 = 0; i1 < help.length; i1++) {
            arr[OneL++]=help[i1];
        }

    }

随机快排

代码

public void sort(int[] arr,int l,int r){
        if (l>=r){
            return;
        }
        //这一步将随机生成的l和r进行交换,复杂度就变成了O(logN+N)
        exchange(arr, l+(int)(Math.random()*(r-l+1)), r);
        int[] process = process(arr, l, r);
        sort(arr,l,process[0]-1);
        sort(arr,process[1]+1,r);
    }
    
    /**
     * @Description: 数组arr, 以arr[r]为分界,<=arr[r]的数在左边,
     *               大于的在右边,等于的在中间,每次排序都会排好等于arr[r]的数,然后再排下一个
     * @Author: zhuyang
     * @Date:  2021-12-26
     * @Param: 
     * @return: 
     **/
    public int[] process(int[] arr,int l,int r){
        if (l>=r){
            return new int[]{-1,-1};
        }
        if (l==r){
            return new int[]{l,r};
        }
        int curL=l-1;
        int currR=r;
        int index=l;
        while (index<currR){
            if (arr[index]>arr[r]){
                exchange(arr,index,--currR);
            }else if (arr[index]<arr[r]){
                exchange(arr,index++,++curL);
            }else {
                index++;
            }
        }
        //最后进行r的交换
        exchange(arr,currR,r);

        //进行返回
        return new int[]{curL+1,currR};
    }

堆排序

代码

public  void heapSort(int[]  arr){
//        for (int i = 0; i < arr.length; i++) {
//            insertValue(arr,i);
//        }
        //该for循环也可以将该数组变成大顶堆
        for (int i = arr.length-1; i >=0; i--) {
            heapify(arr,i,arr.length-1);
        }
        //进行交换,将大顶堆的最开始的非叶子结点进行交换
        exchange(arr,0,arr.length-1);
        //再进行排序,同时排查最末尾元素
        int index=arr.length-1;
        while (index>0){
            heapify(arr,0,index);
            exchange(arr,0,--index);
        }

    }
    /**
     * @Description: 将最大元素排至最初非叶子结点
     * @Author: zhuyang
     * @Date:  2021-12-09
     * @Param:
     * @return:
     **/
    private  void heapify(int[] arr, int i, int index) {
        int left=2*i+1;
        while (index>left&&arr[i]<arr[left]){
            int bigIndex=left+1<index&&arr[left+1]>arr[left]?left+1:left;
            int bigNum=arr[bigIndex]>arr[i]?bigIndex:i;
            //当arr[i]即为最大值时,停止交换
            if (i==bigNum){
                break;
            }
            exchange(arr,i,bigIndex);
            i=bigIndex;
            left=2*i+1;
        }
    }

计数排序

代码

/**
     * @Description: 计数排序 计数排序要求,样本是正整数,且范围比较窄
     * @Author: zhuyang
     * @Date:  2021-12-12
     * @Param:
     * @return:
     **/
    public void sortProcess(int[] arr){
        if (arr==null||arr.length==1){
            return;
        }
        //进行计算最大值
        int num= Integer.MIN_VALUE;
        for (int i : arr) {
            num=Math.max(num,i);
        }
        //进行创建桶
        int[] bucket=new int[num+1];
        for (int i = 0; i < arr.length; i++) {
            bucket[arr[i]]++;
        }
        //进行重新排序
        int index=0;
        for (int i = 0; i < bucket.length; i++) {
            while (--bucket[i]>=0){
                arr[index++]=i;
            }
        }
    }

基数排序

代码

/**
     * @Description: 桶排序思想下的排序:基数排序
     *               基数排序要求,样本是10进制的正整数
     * @Author: zhuyang
     * @Date:  2021-12-26
     * @Param: 
     * @return: 
     **/
    public void sortProcess(int[] arr){
        //求最大值
        int max=Integer.MIN_VALUE;
        for (int i : arr) {
            max=Math.max(max,i);
        }
        //获取其深度
        int depth=getDepth(max);
        //进行再次排序
        sortProcess(arr,0,arr.length,depth);
    }

    private void sortProcess(int[] arr, int l, int r, int depth) {
        //创建10个桶,因为是10进制 0~9

        int[] help=new int[r-l];
        //进行深度遍历
        for (int i = 0; i < depth; i++) {
            int[] barrel=new int[10];
            //进行求arr的每一个数的每一个位数上,总的个数
            for (int i1 = l; i1 < r-l; i1++) {
                barrel[getDigits(arr[i1],i)]++;
            }
            //进行归并和
            for (int i1 = 1; i1 < barrel.length; i1++) {
                barrel[i1]=barrel[i1]+barrel[i1-1];
            }
            //进行入桶
            for (int i1 = l; i1 <  r-l; i1++) {
                int digits = getDigits(arr[i1], i);
                help[barrel[digits]-1]=arr[i1];
                barrel[digits]--;
            }
            System.out.println(Arrays.toString(help));
            //进行出桶 出桶这很重要 一定要遵循先入后出原则
            int index=0;
            for (int i1 = r-l-1; i1 >= 0; i1--) {
                arr[i1]=help[index++];
            }
        }
    }

    public int getDepth(int number){
        int num=0;
        while (number>0){
            number=number/10;
            num++;
        }
        return num;
    }

    /**
     * @Description: 数 1234324 可以分别取出  1 2 3 4 3 2 4
     * @Author: zhuyang
     * @Date:  2021-12-12
     * @Param:
     * @return:
     **/
    public int getDigits(int number,int depth){
        int pow =(int) (number/(Math.pow(10, depth))%10);
        return pow;
    }

稳定性,时间复杂度,空间复杂度

image

总结

基数排序要求,样本是10进制的正整数,计数排序要求样本必须是正整数。

Gitee地址

https://gitee.com/zhuayng/foundation-study/tree/develop/JavaBasis

XFS
原文地址:https://www.cnblogs.com/xiaofengshan/p/15733614.html