【Java】—— 常见的排序算法

一、排序的概念

排序的定义:将一组数据按照特定的顺序重新排列的过程。

排序的目的:是使数据更易于查找、比较和分析。

排序的适应范围:可以应用于各种数据类型。

排序的稳定性:指当出现相同元素时,排序后相同元素的先后顺序保持不变,则称这个排序为稳定排序

内部排序:指在内存中进行排序

外部排序:指借助硬盘完成排序

二、常见排序算法的实现

1、直接插入排序

核心思想:

将数组分为 已排序区间 和 未排序区间 , 初始情况下,已排序区间就是空,未排序区间就是整个数组,每次从 未排序区间 中选择一个元素,插入到 已排序区间 的合适位置上。

代码实现:

java 复制代码
    private static void insertSort(int[] arr){
        //bound 变量表示边界
        //[0,bound) 区间表示有序区间
        //[bound,arr.length) 区间是无序区间
        for(int bound = 1; bound < arr.length ; bound++ ){
            //循环内部进行一次的插入操作
            //value 就是无序区间的第一个元素
            int value = arr[bound];
            //用这个元素在有序区间从后向前比较
            int cur = bound - 1;
            for(;cur >= 0 ; cur--){
                //前一个的值大于bound那么
                if(arr[cur] > value){
                    arr[cur + 1] = arr[cur];
                }else {
                    break;
                }
            }
            //交换元素
            arr[cur+1] = value;
        }
    }

特性:元素集合越接近有序,直接插⼊排序算法的时间效率越⾼

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

空间复杂度:O(1);

稳定性:稳定

2、希尔排序

核心思想:

希尔排序是对插入排序的一种优化,引入了 gap(间隔) 的概念,将元素分成多个组,并分别进行插入排序。再不断缩小 gap 直到 gap 为 1 时,就是普通的插入排序。

代码实现:

java 复制代码
    private static void shellSort(int[] arr){
        //计算一个合适的gap值,再不断缩小
        int gap = arr.length / 2;
        while (gap >= 1){
            insertSortGap(arr,gap);
            gap /= 2;
        }
    }
    private static void insertSort(int[] arr){
        //bound 变量表示边界
        //[0,bound) 区间表示有序区间
        //[bound,arr.length) 区间是无序区间
        for(int bound = 1; bound < arr.length ; bound++ ){
            //循环内部进行一次的插入操作
            //value 就是无序区间的第一个元素
            int value = arr[bound];
            //用这个元素在有序区间从后向前比较
            int cur = bound - 1;
            for(;cur >= 0 ; cur--){
                //前一个的值大于bound那么
                if(arr[cur] > value){
                    arr[cur + 1] = arr[cur];
                }else {
                    break;
                }
            }
            //交换元素
            arr[cur+1] = value;
        }
    }

希尔排序的时间复杂度不好计算,因为 gap 的取值有很多

3、直接选择排序

核心思想:

将元素划分为 有序区间 和 无序区间,初始情况下 有序区间 为 空 ,每一次遍历都从无序区间里找到最小值,把这个最小值依次放到有序区间

代码实现:

java 复制代码
    private static void selectSort(int[] arr){
        for(int bound = 0;bound < arr.length - 1;bound++){
            for (int cur = bound + 1 ; cur < arr.length ; cur++){
                if(arr[cur] < arr[bound]){
                    int temp = arr[cur];
                    arr[cur] = arr[bound];
                    arr[bound] = temp;
                }
            }
        }
    }

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

空间复杂度:O(1)

稳定性:不稳定

4、堆排序

核心思想:

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一 种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。

代码实现:

java 复制代码
    private static void selectSort(int[] arr){
        for(int bound = 0;bound < arr.length - 1;bound++){
            for (int cur = bound + 1 ; cur < arr.length ; cur++){
                if(arr[cur] < arr[bound]){
                    int temp = arr[cur];
                    arr[cur] = arr[bound];
                    arr[bound] = temp;
                }
            }
        }
    }
    private static void headSort(int[] arr){
        //1.建堆
        createHeap(arr);
        int bound = arr.length - 1;
        for(int i = 0; i < arr.length ; i++){
            //交换堆顶和待排序区间的最后一个元素
            int temp = arr[0];
            arr[0] = arr[bound];
            arr[bound] = temp;
            //重新调整堆
            //通过 bound 表示堆的元素个数
            shiftDown(arr,bound,0);
            //最后一个元素归入到 已排序区间
            bound--;
        }
    }
    private static void createHeap(int[] arr){
        //从最后一个非叶子节点触发,从后往前遍历
        for(int i=(arr.length-1-1)/2 ; i >= 0 ; i--){
            shiftDown(arr, arr.length, i);
        }
    }
    private static void shiftDown(int[] arr,int length,int index){
        int parent = index;
        int child = 2 * parent + 1;
        while (child < length){
            //建立大堆,需要取左右子节点中较大值
            if(child + 1 < length && arr[child + 1] > arr[child]){
                child += 1;
            }
            //比较 child 和 parent 元素大小
            if(arr[child] > arr[parent]){
                int temp = arr[child];
                arr[child] = arr[parent];
                arr[parent] = temp;
            }else{
                break;
            }
            parent = child;
            child = 2 * parent + 1;
        }
    }

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

空间复杂度:O(1)

稳定性:不稳定

5、冒泡排序

核心思想:

通过不断比较相邻元素,一趟过后,最大(最小)的一个元素就会在数组中最末尾的位置,再进行 n - 1 次就可以达到有序

代码实现:

java 复制代码
    private static void bubbleSort(int[] arr){
        for(int bound = 0;bound < arr.length-1 ; bound++){
            for (int cur = arr.length - 1 ; cur > bound ; cur--){
                if(arr[cur-1] > arr[cur]){
                    int temp = arr[cur - 1];
                    arr[cur - 1] = arr[cur];
                    arr[cur] = temp;
                }
            }
        }
    }

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

空间复杂度:O(1)

稳定性:稳定

6、快速排序

核心思想:

任取待排序区间中选出某元素作为基准值,按照该排序码将待排序集合分割成两个子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

代码实现:

java 复制代码
    //此处约定[left,right]闭区间为待处理区间
    //快速排序入口
    private static void quickSort(int[] arr){
        quickSort(arr,0, arr.length-1);
    }
    //快速排序辅助递归的方法
    private static void quickSort(int[] arr,int left,int right){
        if(left >= right){
            //待处理区间为空或者只有一个元素,无需排序
            return;
        }
        //针对区间进行整理,返回值 index 表示基准值所在的下标
        int index = partition(arr,left,right);
        //递归左半区间
        quickSort(arr,left,index-1);
        //递归右半区间
        quickSort(arr,index+1,right);
    }
    private static int partition(int[] arr,int left,int right){
        //设定最右侧元素为基准值
        int value = arr[right];
        //设定两个下标,分别从左往右和右往左
        int l = left;
        int r = right;
        while (l < r){
            while (l < r && arr[l] <= value){
                l++;
            }
            while (l < r && arr[r] >= value){
                r--;
            }
            swap(arr,l,r);
        }
        //循环完成交换重合位置和基准值
        swap(arr,l,right);
        return l;
    }
    private static void swap(int[] arr,int i,int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

快速排序在大多数情况下的表现都是非常优秀的,除了个别极端情况会让时间复杂度到O(N^2)

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

空间复杂度:O(logN)

稳定性:不稳定

7、归并排序

核心思想:

通过递归将待排序的数组分成两个子数组,直到每个子数组只包含一个元素。此时,单个元素的数组自然是有序的。将两个有序的子数组合并成一个有序的数组。合并时,从两个子数组的起始位置开始比较,选择较小的元素放入结果数组中,直到其中一个子数组的所有元素都被合并。然后将另一个子数组的剩余元素直接放入结果数组中。

代码实现:

java 复制代码
    //归并排序入口方法
    private static void mergeSort(int[] arr){
            mergeSort(arr,0,arr.length-1);
    }
    private static void mergeSort(int[] arr,int left,int right){
        //如果子区间没有元素或只有一个元素就不再递归
        if(left >= right){
            return;
        }
        //把区间分为等长的两个区间,不强制完全相等
        int mid = (left + right) / 2;
        //递归左半区间
        mergeSort(arr,left,mid);
        //递归右边区间
        mergeSort(arr,mid+1,right);
        //递归走到这,说明已经划分为只剩一个元素的有序数组
        //将有序数组合并
        merge(arr,left,mid,right);
    }
    private static void merge(int[] arr,int left,int mid,int right){
        //创建临时数组保存合并的结果,临时数组的长度应该是 right - left + 1
        int[] result = new int[right - left + 1];
        //通过判断大小尾插到新数组中
        int resultSize = 0;
        //两个下标指向每个区间的开头
        int cur1 = left;
        int cur2 = mid + 1;
        while (cur1 <= mid && cur2 <= right){
            if(arr[cur1] <= arr[cur2]){
                result[resultSize++] = arr[cur1];
                cur1++;
            }else{
                result[resultSize++] = arr[cur2];
                cur2++;
            }
        }
        //处理前面奇数划分不均的情况
        while (cur1 <= mid){
            result[resultSize++] = arr[cur1++];
        }
        while (cur2 <= right){
            result[resultSize++] = arr[cur2++];
        }
        //把临时数组的结果写回到 arr 的 [left,right] 中
        for(int i=0;i<result.length;i++){
            arr[left + i] = result[i];
        }
    }

归并排序的思考更多的是解决在磁盘中的外排序问题

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

空间复杂度:O(N)

稳定性:稳定

三、小结

除了上文提到的这几种,还有许多其他的排序算法,例如 计数排序 ,基数排序 ,桶排序 等等,应该结合实际情况选择使用,学好排序算法的原理可以用这些思想去解决其他的问题。

相关推荐
缘友一世1 小时前
java设计模式[4]之设计型模式
java·开发语言·设计模式
武汉格发Gofartlic1 小时前
如何跟踪FEMFAT许可使用情况
运维·python·算法·信息可视化·数据分析
在未来等你1 小时前
设计模式精讲 Day 5:原型模式(Prototype Pattern)
java·oop·object-oriented-programming·design-patterns·software-engineering·design-pattern-day5·prototype-pattern
污领巾2 小时前
虚幻引擎UE多语言设计与实现
java·数据库·虚幻
m0_640743563 小时前
华为OD-2024年E卷-字符串分割[100分] -- python
数据结构·算法·华为od
sg_knight6 小时前
Spring Cloud LoadBalancer深度解析:官方负载均衡方案迁移指南与避坑实践
java·spring boot·spring·spring cloud·微服务·负载均衡
_何同学6 小时前
Ollama 安装 DeepSeek 与 Spring Boot 集成指南
java·spring boot·后端·ai
剪一朵云爱着7 小时前
力扣面试题 17.05. 字母与数字
算法·leetcode
Code季风8 小时前
跨语言RPC:使用Java客户端调用Go服务端的HTTP-RPC服务
java·网络协议·http·rpc·golang
盖世英雄酱581368 小时前
时间设置的是23点59分59秒,数据库却存的是第二天00:00:00
java·数据库·后端