Java常见的八大排序算法介绍

本文介绍了Java中常见的八大排序算法,包括冒泡排序、选择排序、插入排序、快速排序、归并排序、希尔排序、堆排序和基数排序。详细阐述了各排序算法的原理、特点,如时间复杂度、空间复杂度和稳定性。建议读者通过实例操作加深理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

常用的排序八大算法:

时间复杂度、空间复杂度及稳定性介绍:

img

算法的稳定性

稳定性定义:排序前后两个相等的数相对位置不变,则算法稳定。

稳定性得好处:从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用

1、冒泡排序

原理:
(1)冒泡排序指的是从数组开头的元素开始,与下一个元素依次进行比较,按照所要求的排序规则,在满足要求之后进行交换
(2)对于长度为n的数组,最多循环 n 次进行比较
(3)在进行比较的时候,可能在进行 m 次循环之后就已经满足要求了,这时应该使用flag进行标记,满足要求则不再进行比较
代码如下:

public static void bubbleSort(int[] arr){
        int temp = 0;
        boolean flag = false;
        for(int i = 0;i < arr.length;i++){
            for(int j = 0;j < arr.length-1;j++){
                if(arr[j] > arr[j+1]){//这时升序的情况,如果需要降序排列,则改为小于号
                    flag = true;
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
            if(flag){
                flag = false;
            }else break;
        }
    }

2、选择排序

原理:
(1)选择排序指的是对于给定的数组,每次循环需要选择出最小值,与当前下标所在位置的值进行交换
(2)使用 minIndex 进行最小下标的标记,如果当前下标对应的值就是最小值(minIndex == i),则不用进行交换
代码如下:

public static void selectSort(int[] arr){
        for(int i = 0;i < arr.length-1;i++){
            int minIndex = i;
            int min = arr[i];
            for(int j = i+1;j < arr.length;j++){
                if(min > arr[j]){//此处为升序排序,如果需要降序,则改为小于号
                    minIndex = j;
                    min = arr[j];
                }
            }
            if(minIndex != i){
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
        }
    }

3、插入排序

原理:
(1)插入排序指的是对于给定的数组,需要给当前下标对应的元素找到一个合适的位置,即插入
(2)对于每一个下标,都有对应的插入下标(insertIndex )与插入的值(insertValue ),将 insertValue 与之前的值进行比较,直到找到正确的位置,进行插入
(3)如果插入的位置就是当前的位置(insertIndex+1 == i),则不用插入
代码如下:

public static void insertSort(int[] arr){
        int insertIndex = 0;
        int insertValue = 0;
        for(int i = 1;i < arr.length;i++){
            insertValue = arr[i];
            insertIndex = i-1;
            while(insertIndex >= 0 && insertValue < arr[insertIndex]){//此处为升序,如果要降序排列,则改为小于号
                arr[insertIndex+1] = arr[insertIndex];
                insertIndex--;
            }
            if(insertIndex+1 != i){
                arr[insertIndex+1] = insertValue;
            }
        }
    }

4、快速排序

原理:
(1)快速排序的名字得益于其排序的速度。
(2)每次进行排序的时候,对于给定的左右边界(left、right),找到中间值(pivot),按照排序的规则,找到不满足要求的下标,并将左右下标对应的元素值进行交换。

例如:
对于数组{1,5,-4,3,6,8,4,8,5,4}来说,length = 10,中间位置对应的元素为 8 。
如果要求升序排列的话,就需要让 8 左边的元素都小于 8 ,右边的元素都大于 8 。

(3)在进行交换的时候会出现特殊的情况,比如:

①原来的数组就是有序的情况下,会造成左右部分的下标相等,即 l >= r,这个时候应该结束循环
②如果交换完了之后发现arr[l] = pivot,则 r–
③ 如果交换完了之后发现arr[r] = pivot,则 l++

(4)如果交换完之后,l==r,必须 l++ , r–,否则死循环,栈溢出
(5)在循环结束之后,左右两边都满足要求的情况下,就进行递归操作,将左半部分的元素再次分割为两个部分,进行排序,右半部分同理。
具体代码如下:

public static void quickSort(int[] arr,int left,int right){
        int l = left;
        int r = right;
        int pivot = arr[(left+right)/2];
        int temp = 0;
        //把比pivot小的值放在左边,把比pivot大的值放在右边
        while(l < r){
            //从左边找,一直找到大于等于pivot值
            while(arr[l] < pivot){
                l+=1;
            }
            //从右边找,一直找到小于等于pivot值
            while(arr[r] > pivot){
                r-=1;
            }
            //如果l>=r,说明左右两边已经满足要求:左边全部小于等于pivot,右边全部大于等于pivot
            if(l >= r) break;
            //交换
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            //如果交换完了之后发现arr[l] == pivot,则r--
            if(arr[l] == pivot){
                r-=1;
            }
            //如果交换完了之后发现arr[r] == pivot,则l++
            if(arr[r] == pivot){
                l+=1;
            }
        }
//        System.out.println(Arrays.toString(arr));
        //如果交换完之后,l==r,必须l++,r--,否则死循环,栈溢出
        if(l==r){
            l+=1;
            r-=1;
        }
        //如果最后left是小于r的,那么继续递归进行left-r的排序
        if(left < r){
            quickSort(arr,left,r);
        }
        //如果最后right是大于l的,那么继续递归进行l-right的排序
        if(right > l){
            quickSort(arr,l,right);
        }
    }

5、归并排序

原理:
(1)运用的是分而治之的思想,将给定的数组分为两个部分,对这两个部分的元素进行排序。而这两个部分又继续分别分为两个数组,如此反复,最后不能再继续分割的时候进行合并
代码如下:

//分
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
        if (left < right) {
            int mid = (left + right) / 2;
            mergeSort(arr, left, mid, temp);
            mergeSort(arr, mid + 1, right, temp);
            merge(arr, left, mid, right, temp);
        }
    }
    
	//和
    private static void merge(int[] arr, int left, int mid, int right, int[] temp) {
        int i = left, j = mid + 1, t = 0;
        while (i <= mid && j <= right) {
            if (arr[i] < arr[j]) {
                temp[t] = arr[i];
                i++;
                t++;
            } else {
                temp[t] = arr[j];
                j++;
                t++;
            }
        }
        while (i <= mid) {
            temp[t] = arr[i];
            i++;
            t++;
        }
        while (j <= right) {
            temp[t] = arr[j];
            j++;
            t++;
        }
        t = 0;
        while(left <= right){
            arr[left] = temp[t];
            left++;
            t++;
        }
    }

6、希尔排序

原理:
(1)该方法实质上是一种分组插入方法
(2)把数组按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止。
img
代码如下:

public static void shellSort(int[] arr){
        int temp = 0;
        for(int gap = arr.length/2;gap >0;gap/=2){
            for(int i = gap;i < arr.length;i++){
                for(int j = i - gap;j >= 0;j-=gap){
                    if(arr[j] > arr[j+gap]){//此处为升序排序,降序排序改为小于号即可
                        temp = arr[j];
                        arr[j] = arr[j+gap];
                        arr[j+gap] = temp;
                    }
                }
            }
        }
    }

7、堆排序

原理:
(1)对于一个数组来说,如果要进行堆排序,就是将整个数组看做是一个完全二叉树,比如说数组{50,45,40,20,25,35,30,10,15},变成完全二叉树之后如下:

(2)堆排序中各个元素满足一定的规律:

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

(3)基本思想:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。

代码如下:

public static void heapSort(int[] arr){
        int len = arr.length;
        for(int i = len/2-1;i >= 0;i--){
            adjustHeap(arr,i,len);
        }
        for(int i = len-1;i >= 0;i--){
            int temp = arr[i];
            arr[i] = arr[0];
            arr[0] = temp;
            adjustHeap(arr,0,i);
        }
    }

    public static void adjustHeap(int[] arr,int parent,int length){
        int temp =arr[parent];
        int lChild = 2*parent + 1;
        while(lChild < length){
            int rChild = lChild + 1;
            while(rChild < length && arr[lChild] < arr[rChild]){
                lChild++;
            }
            if(temp >= arr[lChild]) break;
            arr[parent] = arr[lChild];
            parent = lChild;
            lChild = lChild*2+1;
        }
        arr[parent] = temp;
    }

再简单总结下堆排序的基本思路:

a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;

b.将堆顶元素与末尾元素交换,将堆顶元素"沉"到数组末端;

c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

8、基数排序

原理:
(1)基础排序又称为“桶排序”,核心思想是将所有待比较数值统一为同样的长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序,这样从最低位排序一直到最高位排序完成之后,数列就变成了一个有序序列。
(2)首先遍历数组得到最大的元素,以这个最大元素的位数(maxLen)构件一个二位桶数组 bucket ,10行maxLen列
(3)构件一个长度为 10 的一维数组 bucketElementCounts ,用来统计每一个元素出现的次数
(4)遍历数组,找到每一个元素的个位数元素,以元素为行、元素出现的次数为列,放到对应的桶数组中,此时数组 bucketElementCounts 对应位置上的值+1
(5)进行数组的还原,对数组 bucketElementCounts 进行遍历,如果元素不为0,说明有数位分布在该位置,则通过之前的桶数组,将值取出存储到原数组中,最后将数组 bucketElementCounts 对应位置元素置 0
(6)如此反复,直到排序到最高位,即可完成整个数组的排序

代码如下:

public static void radixSort(int[] arr){
        int max = arr[0];
        for(int i = 1;i < arr.length;i++){
            if(arr[i] > max) max = arr[i];
        }
        int maxLen = (max + "").length();
        int[][] bucket = new int[10][arr.length];
        int[] bucketElementCounts = new int[10];
        for(int i = 0,n = 1;i < maxLen;i++,n*=10){
            for(int j = 0;j < arr.length;j++){
                int digitOfElement = arr[j] / n % 10;
                bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
                bucketElementCounts[digitOfElement]++;
            }
            int index= 0;
            for(int k = 0;k < bucketElementCounts.length;k++){
                if(bucketElementCounts[k] != 0){
                    for(int l = 0;l < bucketElementCounts[k];l++){
                        arr[index++] = bucket[k][l];
                    }
                }
                bucketElementCounts[k] = 0;
            }
        }
    }

总结

整个介绍比较详细,但是缺少图的演示,建议自己构造一个数组,一步步的进行推导演示,这样可以帮助理解并且记忆更加深刻。
如果大家看完之后还有什么问题,欢迎评论交流!
如果大家觉得这篇文章写的不错的话,给做着一个赞吧~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值