java中常用排序方法有哪些详解

 更新时间:2025年07月02日 11:08:27   作者:z想不出名字了.  
排序算法经过了很长时间的演变,产生了很多种不同的方法,这篇文章主要介绍了java中常用排序方法有哪些的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

一、排序方法

Java中的常用排序方法有:直接插入排序,希尔排序,冒泡排序,递归排序,堆排序,快速排序,选择排序。

二、分类

稳定性:如果在一个待排序的序列中,有多个相同的元素,经过各种排序方法排序后,该相同元素的相对位置没有发生改变,则称该排序算法是稳定的,反正,则为不稳定。

三.排序方法介绍

1.冒泡排序

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

空间复杂度:O(1)

稳定性:稳定

基本思想:

通过重复比较相邻元素,并在顺序错误时交换它们,将最大(或最小)的元素逐步“冒泡”到序列的一端。重复多轮后,整个序列即变得有序。

基本步骤:

        1.从第一个元素开始,依次比较相邻的两个元素。

        2.如果它们的顺序错误(比如前大后小),就交换它们。

        3.这样一轮下来,最大(或最小)元素会“浮”到最后。

        4.对剩余未排序的部分重复上述步骤,直到整个序列排序完成。

特点:

        1.实现简单,但效率较低,不适合大规模数据的排序。

        2.可以通过加标志优化,提前结束排序。

代码实现:

public static void bubbleSort(int[] array) {
        for (int i = 0; i < array.length-1; i++) {
            boolean flg = false;
            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) {
                return;
            }
        }
    }

2.选择排序

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

空间复杂度:O(1)

稳定性:不稳定

基本思想:

每一轮从待排序的元素中找到最小(或最大)元素,然后将其放到已排序部分的末尾。

基本步骤:

        1.在未排序部分中找到最小(最大)元素;

        2.将其与未排序部分的第一个元素交换;

        3.将已排序部分扩大一个元素,重复上述过程直到全部排序完成。

特点:

        1.算法思路清晰,容易实现,适合学习排序基础;

        2.无论输入数据的初始状态如何,时间复杂度总是 O(n2)O(n2),在大数据集上效率较低;

        3.每轮只进行一次交换,最坏情况下虽然比较多,但移动次数较少,适合数据交换成本较高的场景;

        4.效率低,不适合大规模数据排序。

代码实现:

 private static void swap(int[] array,int i,int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

 public static void selectSort(int[] array) {

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

            for (int j = i+1; j < array.length; j++) {
                if(array[j] < array[minIndex]) {
                    minIndex = j;
                }
            }

            swap(array,i,minIndex);
        }
    }

3.堆排序

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

空间复杂度:O(1)

稳定性:不稳定

基本思想:

堆排序利用堆(特别是最大堆或最小堆)这种数据结构,将待排序数组变成一个堆。利用堆的性质,堆顶元素(最大值或最小值)就是整个数组中的最大值或最小值。通过不断将堆顶元素移到数组末尾,然后调整堆,使剩余元素仍满足堆的性质,从而实现排序。

基本步骤:

        1.建堆:将无序数组构建成一个大根堆(或小根堆);

        2.交换堆顶与末尾元素:将堆顶元素(最大或最小)与数组的最后一个元素交换位置。此时,堆的元素缩小一个容量(排好序的元素在最后);

        3.调整堆:对剩余的元素重新调整为堆,保持堆性质;

        4.重复步骤2和3:直到所有元素都已经排好序。

特点:

        1.原地排序,无需额外空间,时间复杂度稳定在 O(nlog⁡n)O(nlogn),适合大数据集。

        2.不稳定排序(相等元素可能改变原始相对位置),实际常数因堆调整略大于快速排序

        3.不要求稳定性,对空间效率要求较高时。

代码实现:

 public static void heapSort(int[] array) {
        createHeap(array);
        int end = array.length-1;
        while (end > 0) {
            swap(array,0,end);
            siftDown(array,0, end);
            end--;
        }
    }

    private static void createHeap(int[] array) {
        for (int parent = (array.length-1-1)/2; parent >= 0 ; parent--) {
            siftDown(array,parent,array.length);
        }

    }

    private static void siftDown(int[] array,int parent,int len) {
        int child = 2 * parent + 1;
        while (child < len) {
            if(child+1 < len && array[child] < array[child+1]) {
                child++;
            }

            if(array[child] > array[parent]) {
                swap(array,child,parent);
                parent = child;
                child = 2 * parent + 1;
            }else {
                break;
            }

        }
    }

    private static void swap(int[] array,int i,int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

4.直接插入排序

时间复杂度:最好情况O(N),最坏情况O(N^2)

稳定性:稳定

基本思想:

将待排序序列划分为已排序和未排序两部分,逐步将未排序的元素插入到已排序部分的适当位置,直到整个序列有序。

基本步骤:

        1.从第一个元素开始,认为已排序;

        2.取下一个元素,将其与已排序部分的元素从后向前比较,找到合适的位置插入;

        3.将该元素插入到正确位置后,已排序部分扩大一位;

        4.重复以上步骤,直到所有元素都插入完毕。

特点:

        1.简单直观,易于实现。

        2.在数据基本有序时表现较好。

        3.适合单链表排序,不太适合大量数据的排序。

代码实现:

 public static void insertSort(int[] array) {
        for (int i = 1; i < array.length; i++) {
            int tmp = array[i];
            int j = i-1;
            for (; j > 0; j--) {
                if(array[j] > tmp) {
                    array[j+1] = array[j];
                }else {
                    //array[j+1] = tmp;
                    break;
                }
            }
            array[j+1] = tmp;
        }
    }

5.希尔排序

时间复杂度:O(N^1.3) - O(N^1.5)

空间复杂度:O(1)

稳定性:不稳定

基本思想:

希尔排序通过将待排序数组的元素按一定的间隔(称为“步长”或“增量”)进行分组,对每组内的元素进行插入排序。随着排序逐步进行,步长逐渐缩小,直到最后步长为1,即对整个数组进行一次插入排序,从而实现整体排序。

基本步骤:

        1.选择一个初始增量(如数组长度的一半);

        2.将数组元素按增量分组,对每组进行插入排序;

        3.缩小增量;

        4.重复上述步骤,直到增量为1;

        5.最后一次插入排序完成,数组即为有序。

特点:

        1.改善了简单插入排序在大规模数组中的效率,实现简单,性能优于简单插入排序,对部分预排序的数组效果良好。

        2.在最坏情况下性能仍不如归并排序或快速排序,关键在于选择合适的增量序列。

代码实现:

public static void shellSort(int[] array) {
        int gap = array.length;
        while (gap > 1) {
            gap = gap / 2;
            shell(array,gap);
        }
    }
    public static void shell(int[] array,int gap) {
        for (int i = gap; i < array.length; i++) {
            int tmp = array[i];
            int j = i-gap;
            for (; j >= 0; j -= gap) {
                if(array[j] > tmp) {
                    array[j+gap] = array[j];
                }else {
                    break;
                }
            }
            array[j+gap] = tmp;
        }
    }

6.归并排序

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

空间复杂度:O(1)

稳定性:不稳定

基本思想:

归并排序是一种经典的分治算法,其基本思想是将待排序的数组分成两个子数组,分别对这两个子数组递归排序,然后再将排序好的子数组合并成一个有序数组。

基本步骤:

        1.分解:将数组不断二分,直到每个子数组只有一个元素(它本身是有序的);

        2.解决:递归排序左边和右边两个子数组;

        3.合并:将两个有序的子数组合并成一个有序数组。

特点:

        适合处理大数据,尤其是链表或者外部存储时表现优越。

代码实现:

 private static void mergeSortChild(int[] array,int left,int right) {

        if(left >= right) {
            return;
        }
        int mid = (left+right)/2;
        mergeSortChild(array,left,mid);
        mergeSortChild(array,mid+1,right);
        merge(array,left,mid,right);
    }

    private static void merge(int[] array, int left, int mid, int right) {
        int[] tmp = new int[right-left+1];
        int k = 0;
        while (left <= mid && mid+1 <= right) {
            if(array[left] <= array[mid+1]) {
                tmp[k++] = array[left++];
            }else {
                tmp[k++] = array[mid++];
            }
        }

        while (left <= mid) {
            tmp[k++] = array[left++];
        }
        while (mid+1 <= right) {
            tmp[k++] = array[mid++];
        }

        for (int i = 0; i < tmp.length; i++) {
            array[i+left] = tmp[i];
        }

    }

7.快速排序

时间复杂度:最好情况:O(N*logN),最坏情况:O(N^2)

空间复杂度:最好情况:O(logN),最坏情况:O(N)

稳定性:不稳定

基本思想:

一种非常高效且常用的排序算法,由英国计算机科学家托尼·霍尔于1960年提出。它的核心思想是分治策略,通过递归不断将数组划分为更小的部分,最终实现排序。

基本步骤:

        1.选择一个元素作为基准(常用第一个、最后一个或随机选取);

        2.通过划分实现:将数组调整为左边的元素都较小,右边的较大;

        3.递归地对左右子数组排序,直到子数组长度为1或0。

特点:

        1.实现简单,排序快,不占用额外空间;

        2.最佳情况下表现不佳。

代码实现:

有两种方法实现快速排序:

①将区间按照基准值划分成左右两个部分,右边的数据与左边比较,如果右边数据小于左边,则两个数据互换位置。

  private static void quick(int[] array,int start,int end) {
        if(start >= end) {
            return;
        }
        int par = parttion(array,start,end);
        quick(array,start,par-1);
        quick(array,par+1,end);
    }

   private static int parttion(int[] array, int low, int high) {
        int p = array[low];
        int i = low;
        while (low < high) {
            while (low < high && array[high] >= p) {
                high--;
            }
            while (low < high && array[low] <= p) {
                low++;
            }
            swap(array, low, high);
        }
        swap(array,i,low);
        return low;
    }

  private static void swap(int[] array,int i,int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

②将第一个数据存放在临时变量tmp 中,形成一个坑位,然后进行右边与tmp进行比较小于tmp就与tmp位置交换,同理左边也是,与tmp进行比较,最好将空出来的坑位给tmp。

 private static void quick(int[] array,int start,int end) {
        if(start >= end) {
            return;
        }
        int par = parttion(array,start,end);

        quick(array,start,par-1);

        quick(array,par+1,end);
    }


 private static int parttion(int[] array, int low, int high) {
        int tmp = array[low];
        while (low < high) {
            while (low < high && array[high] >= tmp) {
                high--;
            }
            array[low] = array[high];

            while (low < high && array[low] <= tmp) {
                low++;
            }
            array[high] = array[low];
        }

        array[low] = tmp;
        return low;
    }

快速排序的优化:

三数取中:在每次划分时,从待排序数据的第一个元素、最后一个元素和中间元素中,选择中间值(即三者的中位数)作为枢轴(基准值)。

基本步骤:

        1.取待排序数组的左端、右端和中间元素;

        2.求这三个元素的中位数(中值);

        3.将该中值作为枢轴放置到合适位置(通常是数组的最后或特定位置),然后进行快排的划分操作。

特点:

        1.避免在已排序或接近有序的数组中表现差的情况(如全局排序或逆序数组),降低退化为O(n²)的可能性。

        2.提高划分的平衡性,从而缩短递归深度,加快排序速度。

private static void quick(int[] array,int start,int end) {
        if(start >= end) {
            return;
        }

        int index = threeMid(array,start,end);
        swap(array,start,index);
        int par = parttion(array,start,end);
        quick(array,start,par-1);
        quick(array,par+1,end);
    }

private static int threeMid(int[] array,int low, int high) {
        int mid = (low+high)/2;
        if(array[low] < array[high]) {
            if(array[mid] < array[low]) {
                return low;
            }else if(array[mid] > array[high]) {
                return high;
            }else {
                return mid;
            }
        }else{
            if(array[mid] < array[high]) {
                return high;
            }else if(array[mid] > array[low]) {
                return low;
            }else {
                return mid;
            }
        }
    }


 private static int parttion(int[] array, int low, int high) {
        int tmp = array[low];
        while (low < high) {
            while (low < high && array[high] >= tmp) {
                high--;
            }
            array[low] = array[high];

            while (low < high && array[low] <= tmp) {
                low++;
            }
            array[high] = array[low];
        }

        array[low] = tmp;
        return low;
    }

总结 

到此这篇关于java中常用排序方法有哪些的文章就介绍到这了,更多相关java常用排序方法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • mybatis注解开发使用foreach方式

    mybatis注解开发使用foreach方式

    这篇文章主要介绍了mybatis注解开发使用foreach方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • 浅析java中asList的使用详解

    浅析java中asList的使用详解

    Java中的asList方法是数组工具类 Arrays中的一个静态方法,asList()方法把数组转换成集合时,不能使用其修改集合相关的方法,本文通过示例代码给大家介绍java asList使用,感兴趣的朋友一起看看吧
    2021-10-10
  • SpringBoot限制文件或图片上传大小的两种配置方法

    SpringBoot限制文件或图片上传大小的两种配置方法

    这篇文章主要介绍了SpringBoot限制文件或图片上传大小的两种配置方法,一种是配置在启动类中,一种是配置在application.yml或者application.properties中,需要的朋友可以参考下
    2018-03-03
  • 如何对Mysql数据表查询出来的结果进行排序

    如何对Mysql数据表查询出来的结果进行排序

    这篇文章主要介绍了如何对Mysql数据表查询出来的结果进行排序问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • IDEA去除掉代码中虚线、波浪线和下划线实线的方法

    IDEA去除掉代码中虚线、波浪线和下划线实线的方法

    初次安装使用IDEA,总是能看到导入代码后,出现很多的波浪线,下划线和虚线,这是IDEA给我们的一些提示和警告,但是有时候我们并不需要,反而会让人看着很不爽,这里简单记录一下自己的调整方法,供其他的小伙伴在使用的时候参考
    2024-09-09
  • MyBatis Plus 中 update_time 字段自动填充失效的原因分析及解决方案(最新整理)

    MyBatis Plus 中 update_time 字段自动填充失效的原因分析

    在使用MyBatis Plus时,通常我们会在数据库表中设置create_time和update_time两个字段,借助 MyBatis Plus 的自动填充功能来维护这些时间字段,下面通过本文介绍MyBatis Plus 中 update_time 字段自动填充失效的原因分析及解决方案,感兴趣的朋友一起看看吧
    2025-06-06
  • java下载url路径包含中文需要转义的操作

    java下载url路径包含中文需要转义的操作

    这篇文章主要介绍了java下载url路径包含中文需要转义的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • JAVA二叉树的基本操作

    JAVA二叉树的基本操作

    这篇文章主要介绍了JAVA二叉树的基本操作DEMO,想要详情了解的小伙伴请接着看下文吧
    2021-08-08
  • Java多线程间的5种通信方式小结

    Java多线程间的5种通信方式小结

    有两个线程,A 线程向一个集合里面依次添加元素“abc”字符串,一共添加十次,当添加到第五次的时候,希望 B 线程能够收到 A 线程的通知,然后 B 线程执行相关的业务操作,本文介绍的5种通信方式都是基本这两种模型来实现的,需要的朋友可以参考下
    2023-10-10
  • Spring Boot 的注解生效详细步骤解析

    Spring Boot 的注解生效详细步骤解析

    Spring通过扫描、解析、注册分层处理配置类注解(如@Configuration、@ComponentScan等),利用递归、延迟加载和缓存机制,实现BeanDefinition的动态注册与灵活扩展,解决依赖和性能问题,本文给大家介绍Spring Boot的注解生效详细步骤解析,感兴趣的朋友跟随小编一起看看吧
    2025-09-09

最新评论