Common Sort - 常见的几种排序 与 不常见的几种排序 - Java - 细节狂魔

最后

Java架构学习技术内容包含有:Spring,Dubbo,MyBatis, RPC, 源码分析,高并发、高性能、分布式,性能优化,微服务 高级架构开发等等。

还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书+2021年最新大厂面试题。
在这里插入图片描述

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

    // 最后调整

    shell(array,1);

}



public static void main(String[] args) {

    int[] array ={12,5,9,34,6,8,33,56,89,0,7,4,22,55,77};

    shellSort(array);

    System.out.println(Arrays.toString(array));

}

}




![在这里插入图片描述](https://img-blog.csdnimg.cn/c94e5c18be86464db9c04876828da874.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )总结

-----------------------------------------------------------------



> 其实 希尔排序就是一个直接插入排序。



* * *



[]( )选择排序

===================================================================



[]( )直接选择排序 - 原理

--------------------------------------------------------------------------



![在这里插入图片描述](https://img-blog.csdnimg.cn/2093343141ba4f479a522f3afd0fda99.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )优化

-----------------------------------------------------------------



> 定义 一个 变量, 用来记录 此时的 i 后面最小值的下标。等 j 遍历完了,最小值的下标也就拿到了。此时,再进行交换。  

> 这样就不必让上面那样,遇到比 i下标元素 小的,就交换。



* * *



[]( )代码如下

-------------------------------------------------------------------



import java.util.Arrays;

public class SelectSort {

/*

* 稳定性: 不稳定 见附图

* 时间复杂度:O(N^2) 》》 外层循环 n -1,内层循环 n -1

* 空间复杂度:O(1)

* */

public static void selectSort(int[] array){

    for (int i = 0; i < array.length-1; i++) {

        int index = i;

        for (int j = i + 1; j < array.length; j++) {

            if(array[index] > array[j]){

                index = j;

            }

        }

        int tmp = array[i];

        array[i] = array[index];

        array[index] = tmp;

    }

}



public static void main(String[] args) {

    int[] array = {12,6,10,3,5};

    selectSort(array);

    System.out.println(Arrays.toString(array));

}

}




![在这里插入图片描述](https://img-blog.csdnimg.cn/86c18957c2814ea2b806541364b4de47.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



### []( )附图



![在这里插入图片描述](https://img-blog.csdnimg.cn/07891a93b3d14d8fb2c351a8570a4a80.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )双向选择排序 (了解)

==========================================================================



> 每一次从无序区间选出最小 + 最大的元素,存放在无序区间的最前和最后,直到全部待排序的数据元素排完 。  

> ![在这里插入图片描述](https://img-blog.csdnimg.cn/55016c4041bf4e22904dfd6f2d8105b2.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )代码如下

-------------------------------------------------------------------



import java.util.Arrays;

public class SelectSortOP {

public static void selectSortOP(int[] array){

    int low = 0;

    int high = array.length - 1;

    // [low,high] 表示整个无序区间

    while(low < high){

        int min = low;

        int max = low;

        for (int i = low+1; i <= high; i++) {

            if(array[i] < array[min]){

                min = i;

            }

            if(array[i] > array[max]){

                max = i;

            }

        }

        swap(array,min,low);

        if(max == low){

            max = min;

        }

        swap(array,max,high);

        low++;

        high--;

    }



}

public static void swap(int[] array,int x,int y){

    int tmp = array[x];

    array[x] = array[y];

    array[y] = tmp;

}



public static void main(String[] args) {

    int[] array = {9, 5, 2, 7, 3, 6, 8 };

    selectSortOP(array);

    System.out.println(Arrays.toString(array));

}

}




![在这里插入图片描述](https://img-blog.csdnimg.cn/65ac8a342b18416d9cb26dbfb95ac97b.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )堆排序

==================================================================



> 基本原理也是选择排序,只是不在使用遍历的方式查找无序区间的最大的数,而是通过堆来选择无序区间的最大的数。  

> 注意: 排升序要建大堆;排降序要建小堆.  

> 这个我就不讲,因为我在 [堆/优先级]( )中讲的很清楚!  

> 有兴趣的,可以点击 链接关键字 ,跳转到该文章,该内容在 文章目录最后面。  

> 这里我们就直接上代码。  

> ![在这里插入图片描述](https://img-blog.csdnimg.cn/c51cf9dac6ca4cbea62dcacbdb51526a.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )代码

-----------------------------------------------------------------



import java.util.Arrays;

public class HeapSort {

public static void main(String[] args) {

    int[] array = {12,8,5,4,10,15};

    creationHeap(array);// 建堆的时间复杂度:O(N)

    System.out.println(Arrays.toString(array));

    heapSort(array);// 堆排序的时间复杂度:O(N * log2 N)

    // 空间复杂度:O(1)

    System.out.println(Arrays.toString(array));

}

// 创建一个大根堆

public static void creationHeap(int[] array){

    for (int parent = (array.length-1-1)/2; parent >= 0; parent--) {

        shiftDown(array,parent,array.length);

    }

}

public static void heapSort(int[] array){

    /*

    * 时间复杂度:O(N * log2 N)

    * 空间复杂度:O(1)

    * 稳定性:不稳定

    * */

    int end = array.length - 1;

    while(end>0){

        int tmp = array[end];

        array[end] = array[0];

        array[0] = tmp;

        shiftDown(array,0,end);

        end--;

    }

}

// 向下调整

public static void shiftDown(int[] array,int parent,int len){

    int child = parent * 2 + 1;// 做孩纸

    while(child < len){

        // 获取左右子树最大值的下标

        if(child+1 < len && (array[child] < array[child+1])){

            child++;

        }

        if(array[child] > array[parent]){

            int tmp = array[child];

            array[child] = array[parent];

            array[parent] = tmp;

            parent = child;

            child = parent * 2 + 1;

        }else{

            break;

        }

    }

}

}




![在这里插入图片描述](https://img-blog.csdnimg.cn/a514e6489f5b4d1993752d612e987347.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )冒泡排序

===================================================================



![在这里插入图片描述](https://img-blog.csdnimg.cn/3aaef4b04e864a0496ad0d46d9c6bfa8.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )代码如下 - 未优化

-------------------------------------------------------------------------



import java.util.Arrays;

/*

 * 时间复杂度:O(N^2) 【无论是最好情况,还是最坏情况,时间复杂度都不变】

 * 空间复杂度:O(1)

 * 稳定性:稳定【未发生跳跃式交换】

 * */

public class BubbleSort {

public static void bubbleSort(int[] array){

    // 比较的趟数 = 数组的长度 - 1 【 0 ~ 3 一共 4趟】

    for (int i = 0; i < array.length-1; i++) {

        // 比较完一趟后,可以比较的元素个数减一。【因为靠后的数据已经有序】

        // 内循环中,之所以要减一个 1,是因为防止 下面的if语句 发生 数组越界异常

        for(int j = 0;j< array.length-1-i;j++){

            if(array[j] > array[j+1]){

                int tmp = array[j];

                array[j] = array[j+1];

                array[j+1] = tmp;

            }

        }

    }

}



public static void main(String[] args) {

    int[] array = {12,6,10,3,5};

    bubbleSort(array);

    System.out.println(Arrays.toString(array));

}

}




![在这里插入图片描述](https://img-blog.csdnimg.cn/a0e2dddd86c841bca2f3e0e3fa056321.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )代码优化思维

---------------------------------------------------------------------



![在这里插入图片描述](https://img-blog.csdnimg.cn/b21464a29ba441e9945d852d2b909837.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



### []( )代码如下 - 优化



import java.util.Arrays;

public class BubbleSort {

/*

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

* 最好情况【数组有序】可以达到 O(N)

* 空间复杂度:O(1)

* 稳定性:稳定【未发生跳跃式交换】

* */

public static void bubbleSort(int[] array){

    for (int i = 0; i < array.length-1; i++) {

        boolean flag = true;

        for(int j = 0;j< array.length-1-i;j++){

            if(array[j] > array[j+1]){

                int tmp = array[j];

                array[j] = array[j+1];

                array[j+1] = tmp;

                flag = false;// 表示这一趟比较,数组是无序的

            }

        }

        // flag == true

        if(flag){

            break;

        }

    }

}



public static void main(String[] args) {

// 前半段无序,后半段有序

    int[] array = {2,3,1,4,5};

    bubbleSort(array);

    System.out.println(Arrays.toString(array));

}

}




![在这里插入图片描述](https://img-blog.csdnimg.cn/bbfb1463d22443f29aa5a32563bbefda.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )未优化 和 优化代码 运行速度比较

--------------------------------------------------------------------------------



public class BubbleSort {

// 优化

public static void bubbleSort2(int[] array){

    for (int i = 0; i < array.length-1; i++) {

        boolean flag = true;

        for(int j = 0;j< array.length-1-i;j++){

            if(array[j] > array[j+1]){

                int tmp = array[j];

                array[j] = array[j+1];

                array[j+1] = tmp;

                flag = false;

            }

        }

        // flag == true

        if(flag){

            break;

        }

    }

}

// 未优化

public static void bubbleSort1(int[] array){

    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]){

                int tmp = array[j];

                array[j] = array[j+1];

                array[j+1] = tmp;

            }

        }

    }

}



public static void main(String[] args) {

    int[] array = new int[10000];

    for (int i = 0; i < array.length; i++) {

        array[i] = i;

    }

    long start = System.currentTimeMillis();

    bubbleSort2(array);// 优化

    long end = System.currentTimeMillis();

    System.out.println(end - start);// 输出排序所需时间

    

    start = System.currentTimeMillis();

    bubbleSort1(array);// 未优化

    end = System.currentTimeMillis();

    System.out.println(end - start);//输出排序所需时间

}

}




![在这里插入图片描述](https://img-blog.csdnimg.cn/10f1d963ad0843e1aa10fe9d871ac492.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )快速排序 - 重点

========================================================================



[]( )原理

-----------------------------------------------------------------



> 1、从待排序区间选择一个数,作为基准值(pivot)  

> 2、Partition(分割):遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的(可以包含相等的)放到基准值的右边。  

> 3、采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度 == 1.代表已经有序,或者小区间的长度 == 0,代表没有数据。  

> ![在这里插入图片描述](https://img-blog.csdnimg.cn/f7742de46a584397b29ab8b0dade4af4.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )总结

-----------------------------------------------------------------



> 快速排序,其实说白了 和 [二叉树]( ) 很像,先根,再左,后右。利用**递归**去实现!



* * *



[]( )程序框架

-------------------------------------------------------------------



public class QuickSort {

public static void quickSort(int[] array){

    quick(array,0, array.length);

}

public static void quick(int[] array,int start,int end){

    if(start >= end){

        return;

    }

    int pivot = partiton(array,start,end);

    quick(array,start,pivot-1);// 递归左边

    quick(array,pivot+1,end);// 递归右边

}

// 分割 - 找基准

private static int partiton(int[] array,int start,int end){



}

}




* * *



[]( )完善 partition 部分

------------------------------------------------------------------------------



// 分割 - 找基准

private static int partiton(int[] array,int start,int end){

    int tmp = array[start];

    while(start < end){

        while(start < end && array[end] >= tmp){

            end--;

        }

        // 此时 end 下标 元素的值 是 小于 tmp的。

        array[start] = array[end];

        while(start<end && array[start] <= tmp){

            start++;

        }

        //此时 start 下标元素的值 是 大于 tmp的。

        array[end] = array[start];

    }

    // start 和 end 相遇了,将 tmp 赋予 它们相遇下标指向的空间

    array[start] = tmp;

    return start;

}



![在这里插入图片描述](https://img-blog.csdnimg.cn/54c3213f7bff44b5bd538fd88b07e6ea.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



### []( )代码细节部分



![在这里插入图片描述](https://img-blog.csdnimg.cn/7128651879d548ed97890c834439ccd3.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )总程序 - 未优化

------------------------------------------------------------------------



import java.util.Arrays;

public class QuickSort {

/*

* 时间复杂度:O(N^2) 【数据有序或者逆序的情况】

* 最好情况【每次可以均匀的分割待排序序列】:O(N * log2 N)

* 空间复杂度:O(N)[单分支的一棵树]

* 最好:log2 N

* 稳定性:不稳定

* */

public static void quickSort(int[] array){

    quick(array,0, array.length-1);

}

public static void quick(int[] array,int start,int end){

    if(start >= end){

        return;

    }

    int pivot = partiton(array,start,end);

    quick(array,start,pivot-1);// 递归左边

    quick(array,pivot+1,end);// 递归右边

}

// 分割 - 找基准

private static int partiton(int[] array,int start,int end){

    int tmp = array[start];

    while(start < end){

        while(start < end && array[end] >= tmp){

            end--;

        }

        // 此时 end 下标 元素的值 是 小于 tmp的。

        array[start] = array[end];

        while(start<end && array[start] <= tmp){

            start++;

        }

        array[end] = array[start];

    }

    array[start] = tmp;

    return start;

}



public static void main(String[] args) {

    int[] array = {6,1,2,7,9,3,4,5,10,8};

    quickSort(array);

    System.out.println(Arrays.toString(array));

}

}




* * *



[]( )快速排序 的 时间 与 空间复杂度分析

----------------------------------------------------------------------------------



![在这里插入图片描述](https://img-blog.csdnimg.cn/fe03c48592f141dfb6b6d8d1fca81d6f.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )堆排序 与 快排 的区别

---------------------------------------------------------------------------



> 细心的朋友会发现 堆排序 和 快排 的 时间复杂度在最好情况下 都是N\* log2 N。  

> 那么,两者又有什么区别?  

> 堆排序,无论最好还是最坏情况,时间复杂度都是N\* log2 N。空间复杂度 O(1)  

> 那么,又为什么快排 比 堆排序 要快?  

> 其实再细一点说 :在两个排序的时间复杂度都为 N\* log2 N时,其实连着前面还有 一个 k【K \* N\* log2 N 】,只不过快排前面的K要小一点。所以快排要快一点。  

> 在对空间复杂度没有要求的情况: 快排  

> 对空间复杂度有要求的情况,或者说对数据的序列也要要求: 堆排



* * *



[]( )细节拓展

-------------------------------------------------------------------



### []( )if语句中 比较大小的代码中 等号是不能省略的



> 当 下面框选的代码 没有等号时,会造成死循环。  

> ![在这里插入图片描述](https://img-blog.csdnimg.cn/7ed7f695572747e7afa944c455fa7e3f.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)  

> 我就改了一下,末尾元素的值。  

> ![在这里插入图片描述](https://img-blog.csdnimg.cn/8cc1365fb42842469a2e48e33ae6813d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)  

> 那么,问题来了:为什么没有等号就死循环了?  

> ![在这里插入图片描述](https://img-blog.csdnimg.cn/c43456b39a894393bebf6582225db095.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)  

> 所以,在 写快排的时候,比较大小的代码,记住一定要加上等号!!!!!



* * *



### []( )目前版本的 快排代码 不支持 大量数据进行排序 - 会导致栈溢出。



![在这里插入图片描述](https://img-blog.csdnimg.cn/a638bd33cc144998a630d5ea86ccabf8.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



> 这是因为 我们递归的太深了,1百万数据,4百万字节。  

> 1TB等于1024GB;1GB等于1024MB;1MB等于1024KB;1KB等于1024Byte(字节);1Byte等于8bit(位);  

> ![在这里插入图片描述](https://img-blog.csdnimg.cn/1b391963b839464e9f358a7ad34ef172.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)  

> 有的朋友会说:这才多大啊?栈怎么会被挤爆?  

> 这是因为在递归的时候,开辟的栈帧【函数的信息,参数等等等…都有】,所以,每次开辟的栈帧不止 4byte。故栈被挤爆了。  

>    

> 所以,我们要优化快排的 代码。【优化:数据有序的情况】



* * *



[]( )基准值的选择 - 优化前的知识补充

--------------------------------------------------------------------------------



> 1、选择边上(左或者右) 【重点,上面使用的就是这种方法】  

> 2、随机选择(针对 有序数据)【了解】  

> ![在这里插入图片描述](https://img-blog.csdnimg.cn/eb277fd7e9a94a6d8d486e7bf7368e3d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)  

> 3、几数取中(常见的就是三数取中):array\[left\],array\[mid\] ,array\[right\]中 大小为 中间值的为基准值【优化的关键】  

> ![在这里插入图片描述](https://img-blog.csdnimg.cn/c829a5ed67bb4a49b040e146cff97dff.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )快速排序(几数取中法 优化)

-----------------------------------------------------------------------------



import java.util.Arrays;

public class QuickSort {

/*

* 时间复杂度:O(N^2) 【数据有序或者逆序的情况】

* 最好情况【每次可以均匀的分割待排序序列】:O(N * log2 N)

* 空间复杂度:O(N)[单分支情况]

* 最好:log2 N

* 稳定性:不稳定

* */

public static void quickSort(int[] array){

    quick(array,0, array.length-1);

}

public static void quick(int[] array,int start,int end){

    if(start >= end){

        return;

    }

    // 在找基准之前,先确定 start 和 end 的 中间值。[三数取中法]

    int midValIndex = findMidValIndex(array,start,end);

    //将它 与 start 交换。这样后面的程序,就不用改动了。

    swap(array,start,midValIndex);

    int pivot = partiton(array,start,end);

    quick(array,start,pivot-1);// 递归左边

    quick(array,pivot+1,end);// 递归右边

}

// 确定基准值下标

private static int findMidValIndex(int[] array,int start,int end){

    // 确定 start 和 end 的中间下标

    int mid = start + ((end - start)>>>1);// == (start + end)/ 2

    // 确定 mid、start、end 三个下标,谁指向的元素是三个元素中的中间值

    if(array[end] > array[start]){

        if(array[start] > array[mid]){

            return start;

        }else if(array[mid] > array[end]){

            return end;

        }else{

            return mid;

        }

    }else{

        // array[start] >= array[end]

        if(array[end] > array[mid]){

            return end;

        }else if(array[mid] > array[start]){

            return start;

        }else {

            return mid;

        }

    }

}

// 交换两个下标元素

private static void swap(int[] array,int x,int y){

    int tmp = array[x];

    array[x] = array[y];

    array[y] = tmp;

}



// 分割 - 找基准

private static int partiton(int[] array,int start,int end){

    int tmp = array[start];

    while(start < end){

        while(start < end && array[end] >= tmp){

            end--;

        }

        // 此时 end 下标 元素的值 是 小于 tmp的。

        array[start] = array[end];

        while(start<end && array[start] <= tmp){

            start++;

        }

        array[end] = array[start];

    }

    array[start] = tmp;

    return start;

}

// 有序

public static void test1(int capacity){

    int[] array = new int[capacity];

    for (int i = 0; i < capacity; i++) {

        array[i] = i;

    }

    long start = System.currentTimeMillis();

    quickSort(array);

    long end = System.currentTimeMillis();

    System.out.println(end - start);

}



public static void main(String[] args) {

    test1(100_0000);

    int[] array = {6,1,2,7,9,3,4,5,10,6};

    quickSort(array);

    System.out.println(Arrays.toString(array));

}

}




![在这里插入图片描述](https://img-blog.csdnimg.cn/f3f6db43713d4534b3d77c0f9de21755.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



### []( )优化总结



> 1、选择基准值很重要,通常使用几数取中法  

> 2、partition 过程中把和基准值相等的数也选择出来  

> ![在这里插入图片描述](https://img-blog.csdnimg.cn/9d52349878d0477eb598afbc9acbb30e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)  

> 3、待排序区间小于一个阈(yù)值【临界值】

> 

> > 随着不断的划分基准,数组逐渐趋于有序,而区间随着递归也在减小。所以,利用 直接插入排序的特性【越有序越快】,来进一步优化 快排。  

> > ![在这里插入图片描述](https://img-blog.csdnimg.cn/f9a3e9d84c5c407dbecc6bf7cf74eb9b.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )拓展 快速排序 - 非递归实现

------------------------------------------------------------------------------



### []( )非递归实现快速排序的思维



![在这里插入图片描述](https://img-blog.csdnimg.cn/f36693a47de6407e890cd5c20eb8c2f6.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



### []( )代码如下



import java.util.Arrays;

import java.util.Stack;

public class QuickSortNonRecursion {

public static void quickSort(int[] array){

    Stack<Integer> stack = new Stack<>();

    int left = 0;

    int right = array.length-1;

    int pivot = partiton(array,left,right);

    if(pivot > left+1){

        stack.push(left);

        stack.push(pivot-1);

    }

    if(pivot < right -1){

        stack.push(pivot+1);

        stack.push(right);

    }

    while(!stack.isEmpty()){

        right = stack.pop();

        left = stack.pop();

        pivot = partiton(array,left,right);

        if(pivot>left+1){

            stack.push(left);

            stack.push(pivot-1);

        }

        if (pivot<right-1){

            stack.push(pivot+1);

            stack.push(right);

        }

    }

}

public static int partiton(int[] array,int start,int end){

    int tmp = array[start];

    while(start<end){

        while(start<end && array[end] >=tmp){

            end--;

        }

        array[start] = array[end];

        while (start<end && array[start] <= tmp){

            start++;

        }

        array[end] = array[start];

    }

    array[start] = tmp;

    return start;

}



public static void main(String[] args) {

    int[] array = {12,5,8,1,10,15};

    quickSort(array);

    System.out.println(Arrays.toString(array));

}

}




![在这里插入图片描述](https://img-blog.csdnimg.cn/ac37dff9e79c4e309425f298b6c94fe6.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )归并排序 - 重点

========================================================================



[]( )知识铺垫 : 二路合并

--------------------------------------------------------------------------



> 将两个有序表合并成一个有序表,称为二路归并。【简单说就是 将两个有序数组合并为一个有序数组,称为二路合并】  

> ![在这里插入图片描述](https://img-blog.csdnimg.cn/cd8f4fd5277540f28938cae52722dc74.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



### []( )二路合并的代码如下



import java.util.Arrays;

public class MergeSort {

/*

  • array1 已有序

  • array2 已有序

  • */

    public static int[] mergeArrays(int[] array1,int[] array2){

      if(array1 == null || array2 == null){
    
          return array1 == null ? array2: array1;
    
      }
    
      int[] arr = new int[array1.length + array2.length];
    
      int i = 0;// arr 的 遍历变量
    
      int s1 = 0;//array1 的 遍历变量
    
      int s2 = 0;//array2 的 遍历变量
    
      while(s1 < array1.length && s2 < array2.length){
    
          if(array1[s1] > array2[s2]){
    
              arr[i++] = array2[s2++];
    

// s2++;

// i++;

        }else{

            arr[i++] = array1[s1++];

// s1++;

// i++;

        }

    }

    // 循环结束,有一个数组的元素已经全部存入

    // 接下来就是将另一个数组的元素放入 arr 中

    while (s1 < array1.length){

        arr[i++] = array1[s1++];

// i++;

// s1++;

    }

    while (s2 < array2.length){

        arr[i++] = array2[s2++];

// i++;

// s2++;

    }

    return arr;

}



public static void main(String[] args) {

    int[] array1 = {1,6,7,10};

    int[] array2 = {2,3,4,9};

    int[] mergeArray = mergeArrays(array1,array2);

    System.out.println(Arrays.toString(mergeArray));

}

}




![在这里插入图片描述](https://img-blog.csdnimg.cn/536b92dfafa54ce682e297014fccc297.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )归并排序 - 原理

------------------------------------------------------------------------



> 归并排序(MERGE - SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。  

> ![在这里插入图片描述](https://img-blog.csdnimg.cn/811be96025974f299baaa580536bccac.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )难点1 - 如何将一个数组拆分成一个个单独数组【每个数组里只包含一个元素】。

-----------------------------------------------------------------------------------------------------



![在这里插入图片描述](https://img-blog.csdnimg.cn/f6ba509b29124d68a2b2b22f9b108b27.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )难点2 - 合并

-----------------------------------------------------------------------



![在这里插入图片描述](https://img-blog.csdnimg.cn/879af14be93c472d9e7a583983d7e853.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



### []( )归并排序的程序框架



public class MergeSort {

// 归并排序的调用“接口”

public static int[] mergeSort(int[] array){

    if(array == null){

        return array;

    }

    mergeSortFunc(array,0,array.length-1);

    return array;

}

// 归并排序实现

private static void mergeSortFunc(int[] array,int low,int high){

    if(low >= high){

        return;

    }

    // 递归分解

// int mid = (high + low) >>> 1

    int mid = low + ((high - low) >>> 1);

    mergeSortFunc(array,low,mid);// 左边

    mergeSortFunc(array,mid+1,high);// 右边



     // 合并

    merge(array,low,mid,high);

}



private static void merge(int[] array,int low,int mid,int high){



}

}




* * *



### []( )合并程序的完善



> 其实这个并不难,跟我前面做的知识铺垫的思路是一样的。  

> 需要注意的是:  

> 1、我们的参数中 只有一个数组  

> 2、数组 arr ,只是一个临时数组,用来存储 合并之后的结果。  

> 3、在将 arr 数组 存储的结果,转移到 原本数组的时候,注意赋值的位置!



private static void merge(int[] array,int low,int mid,int high){

// 获取 区间之内的元素个数,加一 是因为 零下标元素也算一个元素。

    int[] arr = new int[high - low +1];

    // 左边 区间 【你可以理解为 有序数组 array1的起始与结束下标位置】

    int start1 = low;

    int end1 = mid;

    // 右边 区间【你可以理解为 有序数组 array2的起始与结束下标位置】

    int start2 = mid+1;

    int end2 = high;

    int i = 0;

    while (start1 <= end1 && start2 <= end2){

        if(array[start1] > array[start2]){

            arr[i++] = array[start2++];

        }else{

            arr[i++] = array[start1++];

        }

    }

    while(start1 <= end1){

        arr[i++] = array[start1++];

    }

    while(start2 <= end2){

        arr[i++] = array[start2++];

    }

    // 将 arr 存储的 合并数据,转换到原本数组上。

    // 注意 array 数组中括号的下标的位置。

    for (int j = 0; j < arr.length; j++) {

        array[low++] = arr[j];

    }

}



* * *



#### []( )附图



![在这里插入图片描述](https://img-blog.csdnimg.cn/749ed7c525d64f5caf2794cf9a40d0b5.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )归并排序 - 总程序

-------------------------------------------------------------------------



import java.util.Arrays;

public class MergeSort {

/*

* 时间复杂度:N * log2 N

* 空间复杂丢:O(N)

* 稳定性:稳定

* */

public static int[] mergeSort(int[] array){

    if(array == null){

        return array;

    }

    mergeSortFunc(array,0,array.length-1);

    return array;

}

private static void mergeSortFunc(int[] array,int low,int high){

    if(low >= high){

        return;

    }

// int mid = (high + low) >>> 1

    int mid = low + ((high - low) >>> 1);

    mergeSortFunc(array,low,mid);// 左边

    mergeSortFunc(array,mid+1,high);// 右边

    merge(array,low,mid,high);

}



private static void merge(int[] array,int low,int mid,int high){

    int[] arr = new int[high - low +1];

    int start1 = low;

    int end1 = mid;

    int start2 = mid+1;

    int end2 = high;

    int i = 0;

    while (start1 <= end1 && start2 <= end2){

        if(array[start1] > array[start2]){

            arr[i++] = array[start2++];

        }else{

            arr[i++] = array[start1++];

        }

    }

    while(start1 <= end1){

        arr[i++] = array[start1++];

    }

    while(start2 <= end2){

        arr[i++] = array[start2++];

    }

    for (int j = 0; j < arr.length; j++) {

        array[low++] = arr[j];

    }

}



public static void main(String[] args) {

    int[] array = {1,6,7,10,2,3,4,9};

    mergeSort(array);

    System.out.println(Arrays.toString(array));

}

}




![在这里插入图片描述](https://img-blog.csdnimg.cn/f494532c8fa04b68b4ee8ad5e437537f.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )归并排序 - 时间与空间复杂度分析、稳定性

------------------------------------------------------------------------------------



![在这里插入图片描述](https://img-blog.csdnimg.cn/a994ccf77ebc44229fe7cf8a8cb1abe0.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )归并排序 - 非递归实现

---------------------------------------------------------------------------



![在这里插入图片描述](https://img-blog.csdnimg.cn/4c99b9c1f25746b3a1fbdd023745ddb0.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



### []( )代码如下



import java.util.Arrays;

public class MergeSortNonRecursion {

public static void mergeSort(int[] array){

    //归并排序非递归实现

    int groupNum = 1;// 每组的数据个数

    while(groupNum < array.length){

        // 无论数组含有几个元素, 数组每次都需要从下标 0位置,开始遍历。

        for(int i = 0;i<array.length;i+= groupNum * 2){

            int low = i;

            int mid = low + groupNum -1;



            // 防止越界【每组的元素个数,超过了数组的长度】

            if(mid >= array.length){

                mid = array.length-1;

            }

            int high = mid + groupNum;



            // 防止越界【超过了数组的长度】

            if(high >= array.length){

                high = array.length-1;

            }

            merge(array,low,mid,high);

        }

        groupNum *= 2;//每组的元素个数扩大到原先的两倍。

    }

}

public static void merge(int[] array,int low,int mid,int high){

// high 与 mid 相遇,说明 此时数组分组只有一组,也就说没有另一组的数组与其合并

// 即数组已经有序了,程序不用再往下走。

     if(high == mid){

        return;

    }

    int[] arr = new int[high -low + 1];

    int start1 = low;

    int end1 = mid;

    int start2 = mid+1;

    int end2 = high;

    int i = 0;

    while(start1 <= end1 && start2 <= end2){

        if(array[start1]>array[start2]){

            arr[i++] = array[start2++];

        }else{

            arr[i++] = array[start1++];

        }

    }

    while (start1 <= end1){

        arr[i++] = array[start1++];

    }

    while(start2 <= end2){

        arr[i++] = array[start2++];

    }

    for (int j = 0; j < arr.length; j++) {

        array[low++] = arr[j];

    }

}



public static void main(String[] args) {

    int[] array = {12,5,8,7,3,4,1,10};

    mergeSort(array);

    System.out.println(Arrays.toString(array));

}

}




![在这里插入图片描述](https://img-blog.csdnimg.cn/2932cdc1053c464083caba6d8947ced5.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )海量数据的排序问题

------------------------------------------------------------------------



> 外部排序:排序过程需要在磁盘等外部存储进行的排序  

> 【内部排序:排序过程需要在 内存上进行排序】  

> 前提:内存只有 1G,需要排序的数据有 100G  

> 因为内存中无法把所有数据全部放下,所以需要外部排序,而归并排序是最常用的外部排序。  

> 1、先把文件切分成 200 份,每个512M  

> ![在这里插入图片描述](https://img-blog.csdnimg.cn/e49ebdaa8b5a4279acd8db4707c4b780.png)  

> 2、分别对 512M 的数据量 进行排序,因为 内存已经被分割了,512M < 1G 内存放得下。所以任何排序方式都可以,  

> 3、进行 200 路归并,同时对 200 份有序文件做归并过程,最终结果就有序了  

> ![在这里插入图片描述](https://img-blog.csdnimg.cn/24c9dfd1b20c4e4db2ea84f67210bd6f.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )小总结

==================================================================



> 目前,我们讲了八种排序:直接插入排序、希尔排序、直接选择排序,双向选择排序、冒泡排序,堆排序、快速排序,归并排序。  

> 其中稳定的排序:插入排序,冒泡排序,归并排序,一共三种。  

>    

> 另外,堆排序、归并排序、快速排序的时间复杂度都是 N \* log2 N。  

> 如果,你想速度快,就用快排。  

> 如果,你想稳定,就用归并。  

> 如果,你想空间复杂度低,就用堆排。



* * *



[]( )排序总结

===================================================================



![在这里插入图片描述](https://img-blog.csdnimg.cn/c5940187dd814afcadd7f1905c574546.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



| 排序方法 | 最好(时间复杂度) | 平均(时间复杂度) | 最坏(时间复杂度) | 空间复杂度 | 稳定性 |

| --- | --- | --- | --- | --- | --- |

| 冒泡排序 | O(N) | O(N^2) | O(N^2) | O(1) | 稳定 |

| 插入排序 | O(N) | O(N^2) | O(N^2) | O(1) | 稳定 |

| 选择排序 | O(N^2) | O(N^2) | O(N^2) | O(1) | 不稳定 |

| 希尔排序 | O(N) | O(N^1.3) | O(N^2) | O(1) | 不稳定 |

| 堆排序 | O(N \* log2 N) | O(N \* log2 N) | O(N \* log2 N) | O(1) | 不稳定 |

| 快速排序 | O(N \* log2 N) | O(N \* log2 N) | O(N ^ 2) | O(N) | 不稳定 |

| 归并排序 | O(N \* log2 N) | O(N \* log2 N) | O(N \* log2 N) | O(N) | 稳定 |



* * *



[]( )不常见的排序 - 不基于比较的排序(了解)

====================================================================================



[]( )基数排序

-------------------------------------------------------------------



> 它的思路:假设待排序的数据类型是 整形/十进制数,每个数据 分别按照 个,百,千,万的大小,放入拿出对应编号空间,其最终的结果就是有序的。  

> 放入拿出的次数 取决于 这组数据中 最大值的位数。  

> ![在这里插入图片描述](https://img-blog.csdnimg.cn/572ef459aa0c44449ed2d2b5a0733066.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)



* * *



[]( )代码如下

-------------------------------------------------------------------



import java.util.Arrays;

public class RadixSort {

// 基数排序功能 实际功能实现方法

private static void radixSortFunc(int[] array,int maxDigit){

    int mode = 10 ; // 十进制

本次面试答案,以及收集到的大厂必问面试题分享:

字节跳动超高难度三面java程序员面经,大厂的面试都这么变态吗?

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

xt_Q1NETiBARGFyayBBbmQgR3JleQ==,size_20,color_FFFFFF,t_70,g_se,x_16)


小总结

==================================================================

目前,我们讲了八种排序:直接插入排序、希尔排序、直接选择排序,双向选择排序、冒泡排序,堆排序、快速排序,归并排序。

其中稳定的排序:插入排序,冒泡排序,归并排序,一共三种。

另外,堆排序、归并排序、快速排序的时间复杂度都是 N * log2 N。

如果,你想速度快,就用快排。

如果,你想稳定,就用归并。

如果,你想空间复杂度低,就用堆排。


排序总结

===================================================================

在这里插入图片描述

| 排序方法 | 最好(时间复杂度) | 平均(时间复杂度) | 最坏(时间复杂度) | 空间复杂度 | 稳定性 |

| — | — | — | — | — | — |

| 冒泡排序 | O(N) | O(N^2) | O(N^2) | O(1) | 稳定 |

| 插入排序 | O(N) | O(N^2) | O(N^2) | O(1) | 稳定 |

| 选择排序 | O(N^2) | O(N^2) | O(N^2) | O(1) | 不稳定 |

| 希尔排序 | O(N) | O(N^1.3) | O(N^2) | O(1) | 不稳定 |

| 堆排序 | O(N * log2 N) | O(N * log2 N) | O(N * log2 N) | O(1) | 不稳定 |

| 快速排序 | O(N * log2 N) | O(N * log2 N) | O(N ^ 2) | O(N) | 不稳定 |

| 归并排序 | O(N * log2 N) | O(N * log2 N) | O(N * log2 N) | O(N) | 稳定 |


不常见的排序 - 不基于比较的排序(了解)

====================================================================================

基数排序


它的思路:假设待排序的数据类型是 整形/十进制数,每个数据 分别按照 个,百,千,万的大小,放入拿出对应编号空间,其最终的结果就是有序的。

放入拿出的次数 取决于 这组数据中 最大值的位数。

在这里插入图片描述


代码如下



import java.util.Arrays;



public class RadixSort {

//  基数排序功能 实际功能实现方法

    private static void radixSortFunc(int[] array,int maxDigit){

        int mode = 10 ; // 十进制



# 本次面试答案,以及收集到的大厂必问面试题分享:

[外链图片转存中...(img-1ej9BF35-1715823032542)]

> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/topics/618154847)收录**

**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/topics/618154847)**

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值