iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > JAVA >七大排序算法详解
  • 810
分享到

七大排序算法详解

排序算法算法数据结构 2023-09-10 15:09:33 810人浏览 独家记忆
摘要

1.概念 1.排序的稳定性 常见的稳定的排序有三种:直接插入排序,冒泡排序,归并排序 对于一组数据元素排列,使用某种排序算法对它进行排序,若相同数据之间的前后位置排序后和未排序之前是相同的,我们就

1.概念

1.排序的稳定性

常见的稳定的排序有三种:直接插入排序,冒泡排序,归并排序

对于一组数据元素排列,使用某种排序算法对它进行排序,若相同数据之间的前后位置排序后和未排序之前是相同的,我们就成这种排序算法具有稳定性

单看单个属性的稳定性并无意义,稳定性主要体现在对具有多个属性的对象进行排序时才有意义
假设一个订单对象有两个属性,分别是下单时间与下单金额:
此时我们有一个需求,就是按照订单金额从高到低排序,若金额相同,则按照下单的先后时间排序

  • 方法一:就是先按照订单金额大小排序,然后把金额相同的订单再次按照时间排序,但是这样就需要进行多次排序
  • 方法二:如果我们先把订单按照下单时间的先后排好序,然后再按照订单金额排序,此时如果我们使用的排序是稳定性的,那么它排序后同样是按照下单顺序先后排列的,此时我们就只需要两次排序

所以排序的稳定性也是非常重要

2.排序分类

1.外部排序(不牵扯元素大小比较)

外部排序主要有,桶排序,计数排序,基数排序,时间复杂度近乎O(n)
这三种排序的集合元素非常大,内存放不下,需要使用外部存储器来存储排序,并且对数据的要求非常严格

2.内部排序(基于比较的方式)

在这里插入图片描述

1.插入排序

⭐直接插入排序(稳定)

1.核心思路

每次从无序区间中选择第一个元素,插入到有序区间的合适位置(相当于打扑克牌码牌的过程),有序区间+1,无序区间-1,直至整个数组有序
在这里插入图片描述
⭐⭐⭐直接插入排序在近乎有序的集合性能上非常好(是因为近乎有序的集合,不需要频繁的去交换),常常作为其他高阶排序的优化手段

2.详细代码
        public static void insertSort(int[] nums) {        // 有序区间[0,i)        // 无序区间[i,n)        for (int i = 1; i < nums.length; i++) {            for (int j = i; j-1 >= 0; j--) {                // 若当前值比前一个位置值还小,交换位置                if (nums[j] < nums[j-1]) {                    swap(nums, j , j-1);                }            }        }    }

⭐希尔排序

希尔排序是对插入排序的优化,因为插入排序在近乎有序的数组上性能很好,希尔排序正是利用了这一点

1.核心思路

希尔排序不断地将原数组分为若干个子数组,先将子数组内部调整为有序,不断变大分组的长度,当分组长度为1时(一个元素天然有序),此时进行一次插入排序即可

在这里插入图片描述

2.详细代码
   // 希尔排序    public static void shellSort(int[] arr) {        int gap = arr.length >> 1;        while (gap > 1) {            // 先按照gap分组,组内使用插入排序            insertionSortByGap(arr,gap);            gap = gap >> 1;        }        // 当gap == 1时,整个数组接近于有序,此时来一个插入排序        insertionSortByGap(arr,1);    }    // 按照gap分组,组内的插入排序    private static void insertionSortByGap(int[] arr, int gap) {    // i初始化为0也可以,更容易理解        for (int i = gap; i < arr.length; i++) {            for (int j = i; j - gap >= 0 && arr[j] < arr[j - gap] ; j -= gap) {                swap(arr,j,j - gap);            }        }    }

2.选择排序

⭐直接选择排序(不稳定)

1.核心思路

每次在无序区间选择最小值与无序区间第一个位置元素交换,有序区间+1,无序区间-1,直至整个数组有序
在这里插入图片描述

2.详细代码
        public static void selectionSort(int[] nums) {        // 起始状态:有序区间[0,i)        // 无需区间[i,n)        for (int i = 0; i < nums.length - 1; i++) {            // 指向当前无需区间最小值的下标            int minIndex = i;            for (int j = i+1; j < nums.length; j++) {                // 若当前值比最小值还小                if (nums[j] < nums[minIndex]) {                    // 更新最小值下标                    minIndex = j;                }            }            // 此时minIndex指向无需区间的最小值,将其加载有序区间的后面,有序区间+1,无序区间-1            swap(nums, i, minIndex);        }    }

⭐堆排序

堆排详情博客:
原地堆排序

3.交换排序

⭐冒泡排序(稳定)

1.核心思路

每次遍历将无序数组的最大元素交换至末尾,无序数组-1,有序数组+1,直至整个数组有序

2.详细代码
    // 无序区间[0...i]    // 有序区间[]    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;                    swap(arr,j,j + 1);                }            }            if (!isSwaped) {                break;            }        }    }

⭐快速排序(不稳定)

1.核心思路

每次从无序数组中选取一个元素作为分区点(pivot),将原集合中所有pivot的元素都放在分区点的右侧,这样分区点最终位置就确定了,继续在左半区间和右半区间重复此过程即可
在这里插入图片描述

快速排序和直接选择排序不同的是在快排选择的过程中在不断地调整元素的顺序,在这个过程中原数组已经不断有序了

2.详细代码
        public static void quitSort(int[] nums) {        quitSortInternal(nums, 0, nums.length-1);    }    private static void quitSortInternal(int[] nums, int l, int r) {        if (l >= r) {            return;        }        // 分区之后,返回已经放好位置的元素下标        int p = quitSortByHole(nums, l, r);        // 将放好位置元素的左侧丢进去排序        quitSortInternal(nums, l, p - 1);        // 在将放好位置元素的右侧丢进去排序        quitSortInternal(nums, p + 1, r);        // 此时数组就整体有序了    }            private static void quitSortInternal1(int[] nums, int l, int r) {        Deque<Integer> stack = new ArrayDeque<>();        stack.push(r);        stack.push(l);        while (!stack.isEmpty()) {            // 获取左右区间            int i = stack.pop();            int j = stack.pop();            if (i >= j) {                continue;            }            // 调用获得一个位置的结果            int p = quitSortByHole(nums, i, j);            // 将左边压入栈中            stack.push(p-1);            stack.push(i);            // 右边压入栈中            stack.push(j);            stack.push(p+1);        }    }    private static int quitSortByHole(int[] nums, int l, int r) {        // 分区数字        int pivot = nums[l];        int i = l;        int j = r;        while (i < j) {            while (i < j && nums[j] >= pivot) {                j--;            }            // 走到这说明nums[j] < pivot            nums[i] = nums[j];            while (i < j && nums[i] <= pivot) {                i++;            }            // 走到这说明nums[i] > pivot            nums[j] = nums[i];        }        // 出循环说明i==j        // 将pivot写回i        nums[i] = pivot;        // 返回分区点最终位置        return i;    }

4.归并排序

⭐归并排序(稳定,时间复杂度O(nlogn),空间复杂度O(n))

对原始数据不敏感

1.核心思路

1. 先不断地将原数组一分为二,直至子数组长度为1
2. 不断地将两个相邻的有序子数组合并成一个更大的有序子数组,直至合并后的数组与原数组长度相同,此时排序完成
在这里插入图片描述
核心操作:合并两个子数组merge(nums, l, mid, r)
int i=l代表左边数组走到的下标,
int j=mid+1代表右边数组走到的下标,
k代表当前原数组合并到哪个位置

2.归并排序应用

在这里插入图片描述

3.详细代码
        public static void mergeSort(int[] nums) {        mergeSortInternal(nums, 0, nums.length-1);    }        private static void mergeSortInternal(int[] nums, int l, int r) {        if (l >= r) {            return;        }        int mid = l + ((r - l) >> 1);;        // 将左边和右边丢到merge        mergeSortInternal(nums, l, mid);        mergeSortInternal(nums, mid+1, r);        // 此时说明左右两个数组都已经排好序        merge(nums, l, mid, r);    }    private static void merge(int[] nums, int l, int mid, int r) {        // 创建一个大小与r-l+1一样大的数组        int[] aux = new int[r - l + 1];        System.arraycopy(nums, l, aux, 0, r - l + 1);        int i = l;        int j = mid+1;        // i代表左边数组走到的下标,j代表右边数组走到的下标,k代表当前原数组合并到哪个位置        for (int k = l; k <= r; k++) {            if (i > mid) {                // 说明左边的数组已经走完,把右边的全部写回原数组即可                nums[k] = aux[j-l];                j++;            } else if (j > r) {                // 说明右边的数组已经走完,把左边的全部写回原数组即可                nums[k] = aux[i-l];                i++;            } else if (aux[i-l] <= aux[j-l]) {                // 说明左边的数字小,把左边数组内的数字写回原数组                nums[k] = aux[i-l];                i++;            } else {                // 说明右边的数字小,把右边数组内的数字写回原数组                nums[k] = aux[j-l];                j++;            }        }    }

来源地址:https://blog.csdn.net/m0_57248981/article/details/132451365

--结束END--

本文标题: 七大排序算法详解

本文链接: https://www.lsjlt.com/news/402406.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 七大排序算法详解
    1.概念 1.排序的稳定性 常见的稳定的排序有三种:直接插入排序,冒泡排序,归并排序 对于一组数据元素排列,使用某种排序算法对它进行排序,若相同数据之间的前后位置排序后和未排序之前是相同的,我们就...
    99+
    2023-09-10
    排序算法 算法 数据结构
  • 七大经典排序算法图解
    目录插入排序①直接插入排序基本思想动图演示代码实现②希尔排序基本思想图示代码实现选择排序③直接选择排序基本思想动图演示代码实现④堆排序基本思想建堆需要注意的问题图示代码实现交换排序⑤...
    99+
    2024-04-02
  • JAVA十大排序算法之桶排序详解
    目录桶排序代码实现时间复杂度算法稳定性总结桶排序 桶排序是计数排序的升级,计数排序可以看成每个桶只存储相同元素,而桶排序每个桶存储一定范围的元素,通过函数的某种映射关系,将待排序数组...
    99+
    2024-04-02
  • JAVA十大排序算法之堆排序详解
    目录堆排序知识补充二叉树满二叉树完全二叉树二叉堆代码实现时间复杂度算法稳定性思考总结堆排序 这里的堆并不是JVM中堆栈的堆,而是一种特殊的二叉树,通常也叫作二叉堆。它具有以下特点: ...
    99+
    2024-04-02
  • 七大基于比较的排序算法(JAVA)
    目录 冒泡排序 优化: 堆排序 插入排序 希尔排序 归并排序 快速排序 优化 选择排序   排序算法的稳定性: 大小相同的元素在排序前后相对位置相同就称其为稳定的排序。 注:一个本身就是稳定的排序 是可以实现为不稳定的排序的 ;但是相反...
    99+
    2023-10-11
    算法 排序算法 java
  • JAVA十大排序算法之插入排序详解
    目录插入排序代码实现动图演示代码实现时间复杂度算法稳定性总结插入排序 当我们在玩扑克牌的时候,总是在牌堆里面抽取最顶部的一张然后按顺序在手中排列。 插入排序是指在待排序的元素中,假设...
    99+
    2024-04-02
  • JAVA十大排序算法之快速排序详解
    目录快速排序问题思路荷兰国旗问题代码实现时间复杂度算法稳定性总结快速排序 快速排序是对冒泡排序的一种改进,也是采用分治法的一个典型的应用。JDK中Arrays的sort()方法,具体...
    99+
    2024-04-02
  • JAVA十大排序算法之基数排序详解
    目录基数排序代码实现时间复杂度算法稳定性基数排序 vs 桶排序 vs 计数排序总结基数排序 常见的数据元素一般是由若干位组成的,比如字符串由若干字符组成,整数由若干位0~9数字组成。...
    99+
    2024-04-02
  • JAVA十大排序算法之选择排序详解
    目录选择排序代码实现动图演示代码实现时间复杂度算法稳定性总结选择排序 1.找到数组中最大(或最小)的元素 2.将它和数组的第一个元素交换位置(如果第一个元素就是最大(小)元素那么它就...
    99+
    2024-04-02
  • JAVA十大排序算法之冒泡排序详解
    目录冒泡排序代码实现代码实现时间复杂度算法稳定性总结冒泡排序 1.从数组头开始,比较相邻的元素。如果第一个比第二个大(小),就交换它们两个 2.对每一对相邻元素作同样的工作,从开始第...
    99+
    2024-04-02
  • JAVA十大排序算法之希尔排序详解
    目录希尔排序代码实现时间复杂度算法稳定性总结希尔排序 一种基于插入排序的快速的排序算法。简单插入排序对于大规模乱序数组很慢,因为元素只能一点一点地从数组的一端移动到另一端。例如,如果...
    99+
    2024-04-02
  • JAVA十大排序算法之归并排序详解
    目录归并排序怎么分怎么治代码实现时间复杂度算法稳定性总结归并排序 归并,指合并,合在一起。归并排序(Merge Sort)是建立在归并操作上的一种排序算法。其主要思想是分而治之。什么...
    99+
    2024-04-02
  • JAVA十大排序算法之计数排序详解
    目录计数排序问题代码实现时间复杂度算法稳定性总结计数排序 一种非比较排序。计数排序对一定范围内的整数排序时候的速度非常快,一般快于其他排序算法。但计数排序局限性比较大,只限于对整数进...
    99+
    2024-04-02
  • TypeScript十大排序算法插入排序实现示例详解
    目录一. 插入排序的定义二. 插入排序的流程三. 插入排序的图解四. 插入排序的代码五. 插入排序的时间复杂度六. 插入排序的总结一. 插入排序的定义 插入排序就像是你打扑克牌,你...
    99+
    2023-02-23
    TypeScript插入排序算法 TypeScript 算法
  • Python 十大经典排序算法实现详解
    目录关于时间复杂度关于稳定性名词解释1、冒泡排序(1)算法步骤(2)动图演示(3)Python 代码2、选择排序(1)算法步骤(2)动图演示(3)Python 代码3、插入排序(1)...
    99+
    2024-04-02
  • TypeScript实现十大排序算法之归并排序示例详解
    目录一. 归并排序的定义二. 归并排序的流程三. 归并排序的图解四. 归并排序的代码五. 归并排序的时间复杂度六. 归并排序的总结一. 归并排序的定义 归并排序(merge sor...
    99+
    2023-02-23
    TypeScript算法归并排序 TypeScript算法
  • TypeScript实现十大排序算法之冒泡排序示例详解
    目录一. 冒泡排序的定义二. 冒泡排序的流程三. 冒泡排序的图解四. 冒泡排序的代码五. 冒泡排序的时间复杂度六. 冒泡排序的总结一. 冒泡排序的定义 冒泡排序是一种简单的排序方法...
    99+
    2023-02-23
    TypeScript冒泡排序算法 TypeScript 算法
  • TypeScript十大排序算法之选择排序实现示例详解
    目录一. 选择排序的定义二. 选择排序的流程三. 选择排序的图解四. 选择排序的代码五. 选择排序的时间复杂度六. 选择排序的总结一. 选择排序的定义 选择排序(Selection...
    99+
    2023-02-23
    TypeScript 选择排序算法 TypeScript 算法
  • Java 归并排序算法、堆排序算法实例详解
    基本思想:  归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。归并排序示例:合并方法:设r[i…n]由两个有序子表r[i…m...
    99+
    2023-05-31
    java 归并排序 堆排序
  • java排序算法之选择排序详解
    本文实例为大家分享了java排序算法之选择排序的具体代码,供大家参考,具体内容如下 选择排序 选择排序的思路是这样的:首先,找到数组中最小的元素,拎出来,将它和数组的第一个元素交换位...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作