【JAVA】七大排序算法(图解)

稳定性: 待排序的序列中若存在值相同的元素,经过排序之后,相等元素的先后顺序不发生改变,称为排序的稳定性。

思维导图:

(排序名称后面蓝色字体为时间复杂度和稳定性


1.直接插入排序

核心思路

每次从无序区间中选择第一个元素,插入到有序区间的合适位置,直到整个数组有序。

排序步骤

  1. 定义下标 i 为当前无序区间的第一个元素, i-1 表示有序区间的最大值,下标 j 从后往前遍历有序区间。
  2. 有序区间:[0...i)
  3. 无序区间:[i...n)
  4. 若arr[i]>arr[i-1],直接将arr[i]纳入有序区间即可。
  5. 若arr[i]<arr[i-1],交换arr[i]和arr[i-1],i- -继续比较。

代码

java 复制代码
    public static void insert(int[]arr){
        //有序区间:[0,i)
        //无序区间:[i,n)
        int n=arr.length;
        //i指向当前无序区间的第一个元素
        for (int i = 1; i < n; i++) {
            for (int j = i; j >=1 && arr[j]<arr[j-1]; j--) {
                    int temp=arr[j];
                    arr[j]=arr[j-1];
                    arr[j-1]=temp;
            }
        }
    }

优点

插入排序再近乎有序的集合上性能非常好!!!
只有当前一个元素大于后一个元素时,才需要交换,若前一个元素小于后一个元素,则不需要走第二层循环。


2.希尔排序

核心思路

希尔排序其实是对插入排序的一种优化。

先将待排序的数组分为若干个子数组。将子数组调整为有序状态,不断变大这个分组长度,当最终分组长度为1时,整个数组接近有序。最后来一次插入排序即可。

排序步骤

我们来举一个实例:

  1. 首先gap取5,此时相隔距离为5的元素分到了一组(一共五组,每组两个元素),然后对每一组分别进行插入排序
  1. gap折半为2,此时相隔距离为2的元素被分到了一组(一共两组,每组五个元素),然后对每一组分别进行插入排序
  1. gap再次折半为1,此时所有元素被分到了一组,对它进行插入排序,至此插入排序完成

本例中前两趟就是希尔排序的预排序,最后一趟就是希尔排序的插入排序。

代码

java 复制代码
    private static void insertionSortByGap(int[] arr, int gap) {
        for (int i = gap; i < arr.length; i++) {
            for (int j = i; j-gap>=0 && arr[j]<arr[j-gap]; j-=gap) {
                int temp=arr[j];
                arr[j]=arr[j-gap];
                arr[j-gap]=temp;
            }
        }
    }

3.直接选择排序

核心思路

直接选择排序:每次在无序区间中选择最小值与无序区间的第一个元素交换,直到整个数组有序。

排序步骤

  1. 定义下标 i 为当前无序区间的第一个元素,下标 min 为无序区间的最小值,下标 j 遍历无序区间。
  2. 有序区间:[0...i)
  3. 无序区间:[i...n)
  4. j 遍历无序数组,若 j 指向的元素小于min 指向的元素,则min指向此元素。
  5. 遍历完之后,将min 指向的元素与 i 指向的元素交换。

代码

java 复制代码
    public static void select(int[] arr){
        //有序区间:[0,i)
        //无序区间:[i,n)
        int n=arr.length;
        //当无序区间只剩下一个元素时,已经不用再排了
        for (int i=0; i < n-1 ; i++) {
            //min指向无序区间的最小值
            int min=i;
            for (int j = i+1 ; j < n ; j++) {
                if(arr[j]<arr[min]){
                    min=j;
                }
            }
            //此时min一定指向无序区间的最小值
            int temp=arr[i];
            arr[i]=arr[min];
            arr[min]=temp;
        }
    }

缺点

无论数组是否接近有序,直接选择排序都会执行一遍内部的排序流程,对数据不敏感。


4.堆排序

🌙原地堆排序写在另一篇文章了~

原地堆排序


5.冒泡排序

核心思路

重复扫描待排序序列,并比较每一对相邻的元素,当该对元素顺序不正确时进行交换。一直重复这个过程,直到没有任何两个相邻元素可以交换,就表明完成了排序。

排序步骤

  1. 比较相邻两个数据如果。第一个比第二个大,就交换两个数
  2. 对每一个相邻的数做同样1的工作,这样从开始一队到结尾一队在最后的数就是最大的数。
  3. 针对所有元素上面的操作,除了最后一个。
  4. 重复1~3步骤,直到顺序完成。

代码

java 复制代码
    public static void bubbleSort(int[]arr){
        //外层循环表示要进行元素操作的趟数
        for (int i = 0; i < arr.length-1; i++) {
            boolean isSwaped=false;
            for (int j = 0; j < arr.length-i-1; j++) {
                if(arr[j]>arr[j+1]){
                    isSwaped=true;
                    int temp=arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=temp;
                }
            }
            if(!isSwaped){
                break;
            }
        }
    }

6.快速排序

🌙快速排序写在另一篇文章了~

快速排序详解


7.归并排序

核心思路

1.归: 先不断的将原数组一分为二,直到拆分后的子数组只剩下一个元素。(当数组只有一个元素时,天然有序)

2.并: 不断的将两个连续的有序子数组合并为一个大的数组,直到整个数组合并完成。

排序步骤

并的核心步骤:给定一个临时数组 aux 存储即将归并的子数组的值。

代码

java 复制代码
    public static void mergeSort(int[]arr){
        mergeSortInternal(arr,0,arr.length-1);
    }

    private static void mergeSortInternal(int[] arr, int l, int r) {
        if(l>=r){
            return;
        }
        int mid=l+((r-l)>>2);
        //先将原数组一分为二,在子数组上进行归并排序
        mergeSortInternal(arr,l,mid);
        mergeSortInternal(arr,mid+1,r);
        //此时两个子数组已经有序,将两个子数组合并为原数组
        merge(arr,l,mid,r);
    }

    private static void merge(int[] arr, int l, int mid, int r) {
        //创建一个临时数组
        int[] aux=new int[r-l+1];
        //拷贝子数组的数据到临时数组上
        System.arraycopy(arr,l,aux,0,r-l+1);
        //两个子数组的开始索引
        int i=l;
        int j=mid+1;
        //k表示当前原数组合并到哪个位置
        for (int k = l; k <= r; k++) {
            if(i>mid){
                //此时子数组1全部拷贝完毕,将子数组2的内容全部写回
                arr[k] = aux[j-l];
                j++;
            }else if(j>r){
                //此时子数组2全部拷贝完毕,将子数组1的内容全部写回
                arr[k] = aux[i-l];
                i++;
            }else if(aux[i-l]<=aux[j-l]){
                arr[k]=aux[i-l];
                i++;
            }else{
                arr[k]=aux[j-l];
                j++;
            }
        }
    }

补充:希尔排序的图片参考了这篇博文:希尔排序

相关推荐
谭林杰几秒前
散链表基本操作讲解
数据结构·链表
SKYDROID云卓小助手3 分钟前
无人设备遥控器之无线电频率篇
服务器·网络·单片机·嵌入式硬件·算法
十八岁讨厌编程8 分钟前
【算法训练营Day11】二叉树part1
算法
yi.Ist9 分钟前
数据结构 —— 栈(stack)在算法思维中的巧妙运用
开发语言·数据结构
典孝赢麻崩乐急14 分钟前
Java学习---JVM(1)
java·jvm·学习
m0_5973453123 分钟前
【Android】安卓四大组件之广播接收器(Broadcast Receiver):从基础到进阶
android·java·boradcast·安卓四大组件
程序员的世界你不懂24 分钟前
基于Java+Maven+Testng+Selenium+Log4j+Allure+Jenkins搭建一个WebUI自动化框架(5)失败用例截图与重试
java·selenium·maven
喧星Aries40 分钟前
进程调度的时机,切换与过程方式(操作系统OS)
java·服务器·前端·操作系统·进程调度
JouJz41 分钟前
Spring事务管理深度解析:原理、实践与陷阱
java·spring
此乃大忽悠44 分钟前
身份认证缺陷
java·数据库·webgoat·身份认证缺陷