返回顶部
首页 > 资讯 > 前端开发 > node.js >如何理解排序算法
  • 597
分享到

如何理解排序算法

2024-04-02 19:04:59 597人浏览 安东尼
摘要

这篇文章主要讲解了“如何理解排序算法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解排序算法”吧!排序是我们生活中经常会面对的问题,体育课的时候,老师

这篇文章主要讲解了“如何理解排序算法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解排序算法”吧!

排序是我们生活中经常会面对的问题,体育课的时候,老师会让我们从矮到高排列,考研录取时,成绩会按总分从高到底进行排序(考研的各位读者,你们必能收到心仪学校给你们寄来的大信封),我们网购时,有时会按销量从高到低,价格从低到高的顺序将最符合咱们预期的商品列在前面,这些都是我们生活中的例子。

排序概念:将杂乱无章的数据元素,通过一定的方法(排序算法)按关键字顺序排列的过程叫做排序。例如我们上面的销量和价格就是关键字。

排序算法的稳定性

什么是排序算法的稳定性呢?

因为我们待排序的记录序列中可能存在两个或两个以上的关键字相等的记录,排序结果可能会存在不唯一的情况,所以我们排序之后,如果相等元素之间原有的先后顺序不变。则称所用的排序方法是稳定的,反之则称之为不稳定的。见下图

如何理解排序算法

例如上图,我们的数组中有两个相同的元素 4,  我们分别用不同的排序算法对其排序,算法一排序之后,两个相同元素的相对位置没有发生改变,我们则称之为稳定的排序算法,算法二排序之后相对位置发生改变,则为不稳定的排序算法。

那排序算法的稳定性又有什么用呢?

在我们刷题中大多只是将数组进行排序,只需考虑时间复杂度,空间复杂度等指标,排序算法是否稳定,一般不进行考虑。但是在真正软件开发中排序算法的稳定性是一个特别重要的衡量指标。继续说我们刚才的例子。我们想要实现年终奖从少到多的排序,然后相同年终奖区间内的红豆数也按照从少到多进行排序。

排序算法的稳定性在这里就显得至关重要。这是为什么呢?见下图

如何理解排序算法

第一次排序之后,所有的职工按照红豆数从少到多有序。

第二次排序中,我们使用稳定的排序算法,所以经过第二次排序之后,年终奖相同的职工,仍然保持着红豆的有序(相对位置不变),红豆仍是从小到大排序。我们使用稳定的排序算法,只需要两次排序即可。

稳定排序可以让第一个关键字排序的结果服务于第二个关键字排序中数值相等的那些数。

上述情况如果我们利用不稳定的排序算法,实现这一效果是十分复杂的。

比较类和非比较类

我们根据元素是否依靠与其他元素的比较来决定元素间的相对次序。以此来区分比较类排序算法和非比较类排序算法。

内排序和外排序

内排序是在排序的整个过程中,待排序的所有记录全部被放置在内存中。外排序是由于排序的记录个数太多,不能同时放置在内存中,整个排序过程需要在内外存之间多次交换数据才能进行,常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。

对我们内排序来说,我们主要受三个方面影响,时间性能,辅助空间,算法的复杂性

时间性能

在我们的排序算法执行过程中,主要执行两种操作比较和交换,比较是排序算法最起码的操作,移动指记录从一个位置移动到另一个位置。所以我们一个高效的排序算法,应该尽可能少的比较和移动。

辅助空间

执行算法所需要的辅助空间的多少,也是来衡量排序算法性能的一个重要指标

算法的复杂度

这里的算法复杂度不是指算法的时间复杂度,而是指算法本身的复杂度,过于复杂的算法也会影响排序的性能。

下面我们一起先来复习两种简单排序算法,冒泡排序和简单选择排序,看看有没有之前忽略的东西。后面会持续连载,把常见的和实用的排序算法都进行总结

冒泡排序(Bubble Sort)

我们在各个算法书上学习排序时,第一个估计都是冒泡排序。主要是这个排序算法思路最简单,也最容易理解,(也可能是它的名字好听,哈哈),学过的老哥们也一起来复习一下吧,我们一起深挖一下冒泡排序。

冒泡排序的基本思想是,两两比较相邻记录的关键字,如果是反序则交换,直到没有反序为止。冒泡排序一次冒泡会让至少一个元素移动到它应该在的位置,那么如果数组有  n 个元素,重复 n 次后则一定能完成排序。根据定义可知那么冒泡排序显然是一种比较类排序。

最简单的排序实现

我们先来看一下这段代码

class Solution {     public int[] sortArray(int[] nums) {         int len = nums.length;         for (int i = 0; i < len; ++i) {             for (int j = i+1; j < len; ++j) {                 if (nums[i] > nums[j]) {                     swap(nums,i,j);                 }             }         }         return nums;               }     public void swap(int[] nums,int i,int j) {         int temp = nums[i];         nums[i] = nums[j];         nums[j] = temp;     } }

我们来思考一下上面的代码,每次让关键字 nums[i] 和 nums[j] 进行比较如果 nums[i] > nums[j] 时则进行交换,这样  nums[0] 在经过一次循环后一定为最小值。

那么这段代码是冒泡排序吗?显然不是,我们冒泡排序的思想是两两比较相邻记录的关键字,注意里面有相邻记录,所以这段代码不是我们的冒泡排序,下面我们用动图来模拟一下冒泡排序的执行过程,看完之后一定可以写出正宗的冒泡排序。

冒泡排序代码

class Solution {     public int[] sortArray(int[] nums) {         int len = nums.length;         for (int i = 0; i < len; ++i) {             for (int j = 0; j < len - i - 1; ++j) {                 if (nums[j] > nums[j+1]) {                     swap(nums,j,j+1);                 }             }         }         return nums;            }     public void swap(int[] nums,int i,int j) {         int temp = nums[i];         nums[i] = nums[j];         nums[j] = temp;     } }

上图中的代码则为正宗的冒泡排序代码,但是我们是不是发现了这个问题

如何理解排序算法

我们此时数组已经完全有序了,可以直接返回,但是动图中并没有返回,而是继续执行,那我们有什么办法让其完全有序时,直接返回,不继续执行呢?

我们设想一下,我们是通过 nums[j] 和 nums[j+1]  进行比较,如果大于则进行交换,那我们设想一下,如果一个完全有序的数组,我们进行冒泡排序,每次比较发现都不用进行交换。那么如果没有发生交换则说明当前完全有序。

那我们可不可以通过一个标志位来进行判断是否发生了交换呢?当然是可以的

我们来对冒泡排序进行改进

改进的冒泡排序代码

class Solution {     public int[] sortArray (int[] nums) {         int len = nums.length;         //标志位         boolean flag = true;         //注意看 for 循环条件         for (int i = 0; i < len && flag; ++i) {             //如果没发生交换,则依旧为false,下次就会跳出循环             flag = false;             for (int j = 0; j < len - i - 1; ++j) {                 if (nums[j] > nums[j+1]) {                     swap(nums,j,j+1);                     //发生交换,则变为true,下次继续判断                     flag = true;                 }             }                   }         return nums;              }     public void swap (int[] nums,int i,int j) {         int temp = nums[i];         nums[i] = nums[j];         nums[j] = temp;     } }

这样我们就避免掉了已经有序的情况下无意义的循环判断。

时间复杂度分析

最好情况,就是要排序的表完全有序的情况下,根据改进后的代码,我们只需要一次遍历即可,只需 n -1 次比较,时间复杂度为 O(n)。

最坏情况时,即待排序表逆序的情况,则需要比较(n-1) + (n-2) +.... + 2 + 1= n*(n-1)/2  ,并等量级的交换,则时间复杂度为O(n^2)。

平均情况下,需要 n*(n-1)/4 次交换操作,比较操作大于等于交换操作,而复杂度的上限是 O(n^2),所以平均情况下的时间复杂度就是  O(n^2)。

空间复杂度分析

因为冒泡排序只是相邻元素之间的交换操作,只用到了常量级的额外空间,所以空间复杂度为 O(1)

稳定性分析

那么冒泡排序是稳定的吗?当然是稳定的,我们代码中,当 nums[j] > nums[j + 1]  时,才会进行交换,相等时不会交换,相等元素的相对位置没有改变,所以冒泡排序是稳定的。

如何理解排序算法

简单选择排序(Simple Selection Sort)

我们的冒泡排序不断进行交换,通过交换完成最终的排序,我们的简单选择排序的思想也很容易理解,主要思路就是我们每一趟在 n-i+1  个记录中选取关键字最小的记录作为有序序列的第 i 个记录,见下图

如何理解排序算法

例如上图,绿色代表已经排序的元素,红色代表未排序的元素。我们当前指针指向 4 ,则我们遍历红色元素,从中找到最小值,然后与 4  交换。我们发现选择排序执行完一次循环也至少可以将 1 个元素归位。

下面我们来看一下代码的执行过程,看过之后肯定能写出代码的。

注:我们为了更容易理解,min 值保存的是值,而不是索引,实际代码中保存的是索引

简单选择排序代码

class Solution {     public int[] sortArray(int[] nums) {          int len = nums.length;         int min = 0;         for (int i = 0; i < len; ++i) {             min = i;             //遍历找到最小值             for (int j = i + 1; j < len; ++j) {                               if (nums[min] > nums[j]) min = j;                           }             if (min != i) swap(nums,i,min);                 }         return nums;     }     public void swap (int[] nums, int i, int j) {         int temp = nums[i];         nums[i] = nums[j];         nums[j] = temp;     } }

时间复杂度分析

从简单选择排序的过程来看,他最大的特点就是交换移动元素次数相当少,这样也就节省了排序时间,简单选择和冒泡排序不一样,我们发现无论最好情况和最坏情况,元素间的比较次数是一样的,第  i 次排序,需要 n - i 次比较,n 代表元素个数,则一共需要比较(n-1) + (n-2) +.... + 2 + 1= n*(n-1)/2 次。

对于交换而言,最好情况交换 0 次,最坏情况(逆序时)交换 n - 1次。那么简单选择排序时间复杂度也为 O(n^2)  但是其交换次数远小于冒泡排序,所以其效率是好于冒泡排序的。

空间复杂度分析

由我们的动图可知,我们的简单选择排序只用到了常量级的额外空间,所以空间复杂度为 O(1)。

稳定性分析

我们思考一下,我们的简单选择排序是稳定的吗?

显然不是稳定的,因为我们需要在指针后面找到最小的值,与指针指向的值交换,见下图。

如何理解排序算法

此时我们需要从后面元素中找到最小的元素与指针指向元素交换,也就是元素 2 。但是我们交换后发现,两个相等元素 3  的相对位置发生了改变,所以简单选择排序是不稳定的排序算法。

如何理解排序算法

感谢各位的阅读,以上就是“如何理解排序算法”的内容了,经过本文的学习后,相信大家对如何理解排序算法这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: 如何理解排序算法

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

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

猜你喜欢
  • 如何理解排序算法
    这篇文章主要讲解了“如何理解排序算法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解排序算法”吧!排序是我们生活中经常会面对的问题,体育课的时候,老师...
    99+
    2024-04-02
  • Java排序算法之堆排序如何实现
    这篇文章主要介绍了Java排序算法之堆排序如何实现,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。二叉堆是完全二叉树或者是近似完全二叉树。二叉堆满足二个特性︰1.父结点的键值总...
    99+
    2023-06-21
  • Java 数组中的冒泡排序算法如何理解
    这篇文章跟大家分析一下“Java 数组中的冒泡排序算法如何理解”。内容详细易懂,对“Java 数组中的冒泡排序算法如何理解”感兴趣的朋友可以跟着小编的思路慢慢深入来阅读一下,希望阅读后能够对大家有所帮助。下面跟着小编一起深入学习“Java ...
    99+
    2023-06-02
  • 排序算法之插入排序法解析
    目录什么是插入排序法算法优化心得体会什么是插入排序法 插入排序法是一种简单但有效的排序算法,其基本思想是将一个待排序的元素逐个插入到已经排好序的元素序列中,直至所有元素都被插入完成,...
    99+
    2023-08-08
    排序算法 插入排序法
  • 排序算法之希尔排序法解析
    目录什么是希尔排序法希尔排序法与插入排序法之间的区别与联系代码演示对比什么是希尔排序法 希尔排序法(Shell Sort),也称为缩小增量排序,是一种改进的插入排序算法。它通过将待排...
    99+
    2023-08-08
    希尔排序算法 希尔排序法
  • Python排序算法之堆排序算法
    目录1. 树满二叉树的特性:什么是完全二叉树?完全二叉树的专业概念:2. 二叉堆2.1 二叉堆的抽象数据结构2.2 API 实现3. 堆排序4. 后记本文从树数据结构说到二叉堆数据结...
    99+
    2023-01-07
    python堆排序算法实现 堆排序算法以及python实现 python 堆排序算法
  • 图解Java排序算法之堆排序
    目录预备知识堆排序堆堆排序基本思想及步骤再简单总结下堆排序的基本思路:总结预备知识 堆排序 堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均...
    99+
    2024-04-02
  • Java 归并排序算法、堆排序算法实例详解
    基本思想:  归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。归并排序示例:合并方法:设r[i…n]由两个有序子表r[i…m...
    99+
    2023-05-31
    java 归并排序 堆排序
  • Java排序算法之计数排序如何实现
    这篇文章主要为大家展示了“Java排序算法之计数排序如何实现”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Java排序算法之计数排序如何实现”这篇文章吧。计数排序是非比较的排序算法,用辅助数组对...
    99+
    2023-06-21
  • C++快速排序算法简明理解
    目录一、问题描述二、想法三、算法实现总结一、问题描述 [问题] 应用快速排序方法对一个记录序列进行升序排列。快速排序(quick sort)的分治策略如下。 (1)划分:选定一个记录...
    99+
    2024-04-02
  • java排序算法之选择排序详解
    本文实例为大家分享了java排序算法之选择排序的具体代码,供大家参考,具体内容如下 选择排序 选择排序的思路是这样的:首先,找到数组中最小的元素,拎出来,将它和数组的第一个元素交换位...
    99+
    2024-04-02
  • 图解Java排序算法之归并排序
    目录基本思想合并相邻有序子序列代码实现总结基本思想 归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(...
    99+
    2024-04-02
  • 图解Java排序算法之希尔排序
    目录基本思想代码实现总结希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小...
    99+
    2024-04-02
  • 排序算法图解之Java选择排序
    目录1.选择排序简介2.图解选择排序算法3.选择排序代码实现1.选择排序简介 选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是:第一次从待排序的数据元...
    99+
    2022-11-13
    Java选择排序 Java 排序
  • 排序算法图解之Java插入排序
    目录1.插入排序简介2.插入排序思想及图解3.插入排序代码实现1.插入排序简介 插入排序,一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法。插入排序是一种最简单的排序...
    99+
    2022-11-13
    Java 插入排序算法 Java插入排序 Java 排序算法
  • 排序算法图解之Java希尔排序
    目录1.希尔排序简介2.希尔排序算法图解3.希尔排序代码实现1.希尔排序简介 希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法,其也是一种特殊的插入排序,即将...
    99+
    2022-11-13
    Java 希尔排序 Java 排序算法 Java 排序
  • PHP如何用“自然排序”算法对数组排序
    这篇文章将为大家详细讲解有关PHP如何用“自然排序”算法对数组排序,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。自然排序算法 自然排序算法是一种对字符串数组进行排序的算法,其结果与人类按自然顺序阅读字符串...
    99+
    2024-04-02
  • 如何使用归并排序算法
    本篇内容介绍了“如何使用归并排序算法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!归并排序算法思路要将一个...
    99+
    2024-04-02
  • JAVA十大排序算法之堆排序详解
    目录堆排序知识补充二叉树满二叉树完全二叉树二叉堆代码实现时间复杂度算法稳定性思考总结堆排序 这里的堆并不是JVM中堆栈的堆,而是一种特殊的二叉树,通常也叫作二叉堆。它具有以下特点: ...
    99+
    2024-04-02
  • JAVA十大排序算法之桶排序详解
    目录桶排序代码实现时间复杂度算法稳定性总结桶排序 桶排序是计数排序的升级,计数排序可以看成每个桶只存储相同元素,而桶排序每个桶存储一定范围的元素,通过函数的某种映射关系,将待排序数组...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作