| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 数据结构与算法 -> 基础排序算法------内部排序 -> 正文阅读 |
|
[数据结构与算法]基础排序算法------内部排序 |
目录 leetcode 451 根据字符出现频率排序 计数排序的应用 1 基础概念排序问题,就是将无序序列排成一个有序序列(升序或降序)。 2 插入排序基本思想即边插入边排序,每步将一个待排序的对象,按其关键码大小,插入到前边已经排好序的一组对象的适当位置上,直到全部对象全部插入为止,插入过程中始终保持当前子序列有序。 虽说是边插入边排序,貌似一边输入一遍插入排序,像打牌一样,但通常情况下我们都是拿到一个待排序的无序序列,然后进行排序后输出有序序列。即真实过程并不是我们一遍输入一遍排序,而是给定一个无需序列a: 在插入a[i]前,数组a的前半段(a[0]~a[i-1])是有序段,后半段(a[i]~a[n-1])是停留在输入次序的无序段。 插入a[i]使a[0]~a[i]有序,就是要为a[i]找到一个插入位置j(0<=j<=i),将a[i]插入到a[j]的位置上之后,有序子序列长度加1. 可见插入排序的关键在于确定插入位置的方法,基于插入方法的不同,插入排序可分为三种:直接插入排序、二分插入排序、希尔排序。 直接插入排序直接插入排序概述主要思想是用顺序查找法查找到插入位置然后插入。 比如上图中一个待排序的数列,i前边的部分是已经排好序的部分,i后边的部分是未排序的。当需要把i插入到前边完成排序时,先复制i的值,然后从 j = i - 1的位置往前找,如果j位置上元素大于i位置上元素,就将j位置上元素后移,然后j前移。这也是为什么我们要先复制i位置的值的原因,因为前边元素后移会覆盖后边元素。 因此主要代码逻辑为:
以上代码在每次循环中都要进行两次比较,一次是待插入元素与当前遍历元素的比较,一次是判断j是否是有效索引的比较,类似顺序查找中的做法,我们可以设置一个哨兵,来减少一次比较。使用哨兵之后,就不用判断索引j的有效性了。具体做法如下,即每次都将待插入的元素复制到数组开头作为哨兵。
直接插入排序复杂度分析实现排序的基本操作有两个,一个是比较序列中两个关键字的大小,一个是移动序列中的记录。 最好的情况:原始序列中关键字顺序有序 最坏的情况:原始序列中关键字逆序有序 平均的情况: 最坏情况下和平均情况下时间复杂度都是平凡阶,最优情况当然是线性阶。 空间复杂度常数阶。 是一种稳定的排序方法。 二分插入排序主要思想直接插入排序中用顺序查找来找到插入位置,二分插入排序则使用二分查找来确定插入位置。 因为i位置之前是已经拍好序的,因此可以用二分查找。
二分插入排序效率分析首先,由于二分查找比顺序查找效率高,所以二分插入排序平均性能肯定是超过直接插入排序的。 具体还是要看比较次数与移动次数。 比较次数: 二分查找的比较次数值与对象个数有关,与数列原始排列无关。 当n较大时,总的比较次数比直接插入排序最坏情况好的多,但是比其最好情况要差。 当初始排列已经有序或接近有序时,直接插入排序比二分插入排序比较次数好(其实就是直接插入的最好情况要比二分插入排序好)。 移动次数: 二分插入排序移动次数与直接插入排序一样,依赖于初始排列。 即二分插入排序优化了比较次数,但是没有优化移动次数。 希尔排序基本思想在直接插入排序中,我们每次插入一个元素,都要把前边有序部分依次遍历一遍。并且我们知道,当原序列基本有序时,直接插入排序效率会很高。因此提出了希尔排序来从这两方面改进直接插入排序: ? ? ? ? 先将整个待排记录序列分割成若干子序列(子序列中元素对应到原序列中元素不连续),分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一个直接插入排序。 也就是说希尔排序实际上是进行了多遍插入排序,且刚开始时候的插入排序对应的元素在元素列中间隔更大,因此可以依次把元素移动更多步数。 看下边例子: 第一次子序列插入排序:如图中5-间隔那一行中3个蓝色方块81,35,41,它们就是本次直接插入排序的序列,是在原序列中每五个元素抽一个形成的一个子序列。直接插入排序完成后就形成了35,41,81的排序。然后再从94开始,每5个元素抽一个组成子序列92,17,75,继续进行直接插入排序,接下俩还有11,95,15,等子序列待排序。本次插入排序可以一次让元素移动5步,而不是一步一步移动的。 第二次子序列插入排序:从图中5间隔排好序的首元素35开始,每3个元素抽一个元素组成子序列,仍进行直接插入排序。然后从17开始,每3个元素抽取子序列,直接插入排序。一直到序列中所有元素都完成了3间隔的直接插入排序。 最后一次子序列插入排序:最后一个1间隔直接插入排序,其实就是直接对整个序列进行直接插入排序,但是由于上边5间隔和3间隔排序的完成,此时序列已经基本有序了,因此最后进行直接插入排序时候的效率会很高。 希尔排序的特点有两个: ? ? ? ? 缩小增量; ? ? ? ? 多遍插入排序; 多遍插入排序就不说了,缩小增量的意思是,多轮插入排序中的增量序列是递减的,且增量序列是互质的,最后一个必须是1。 希尔排序复杂度分析希尔排序算法效率与增量序列的取值有关,但是小于最坏情况还是小于直接插入排序的平方阶的。但希尔排序是一种不稳定的排序算法。希尔排序不适合在链式存储结构上实现。 总结: 3 交换排序基本思想基于插入的方法主要操作是比较和移动,待插入元素与当前元素比较,如果当前元素大于待插入元素,则将当前元素后移。而基于交换的排序主要思想是将序列中两个元素进行比较,如果他们之间的大小顺序与我们要排的顺序是相反的,则将他们交换。即基于交换的排序方法主要操作是比较和交换。 冒泡排序冒泡思想每趟比较相邻位置的两个元素,然后按规则交换,对于一个含有m个元素的序列,只需要5趟比较就可以完成排序。如下图,第一趟结束,我们已经将整个序列中最大值放在了最后边,第二趟的时候不用再比较最后一个值,因为那个一定是最大的,因此只需要比较前m-1个值就行了,等到低5趟的时候,只需要比较前两个值的相对大小就行了。 冒泡特点每趟结束时,都能挤出一个最大值到最后边位置,同时还能部分理顺其它元素。 但是有时候对于m个记录的序列,我们不需要m-1趟交换。 如果在某一趟的时候我们发现没有发生记录交换,那么说明整个序列已经排好序了,后边的趟就不用进行了,直接结束算法。 冒泡效率分析最好情况: 序列本来就是有序的,这样一趟下来发现一次交换操作也没做,直接结束算法。 最坏情况: 序列逆序,第一趟比较n-1次,第二趟比较n-2次,第三趟比较n-3次,,,最后一趟比较1次,等差数列求和。 快速排序基本思想枢轴一般选择第一个数就行。 快排复杂度快速排序的时候原始序列越乱越好,越有序则性能越差 C++冒泡&快排实现通常用两个函数实现,一个用来递归实现整个序列的排序,一个用来进行快排中的一轮排序。一轮排序指的是在一个子表中选取一个枢轴,然后将比其小的数放在左边,比起大的数放在右边,具体可以参考数据结构与算法基础(青岛大学-王卓)_哔哩哔哩_bilibili
4 选择排序简单选择排序基本操作就是每次找到当前最小的,然后交换位置。 简单选择排序复杂度分析堆排序堆的定义这里虽然堆的实际是一个完全二叉树,但是不要把堆理解的窄了,只要满足一个序列满足上边的条件那么这个序列就是一个堆,所以堆的本质可以说就是一个满足特定条件的序列,只是可以对应到一个完全二叉树上来帮助理解。所以在堆排序中实际操作的数据结构还是数组,而不是树。 堆排序基本操作要使用堆排序,主要要解决两个问题, 1. 如何由一个无序序列建成一个堆 2. 如果在输出堆顶元素后,调整剩余元素为一个新的堆 其中第二个问题解决了,第一个问题就解决了,可以通过不断调整,将无序序列建成一个堆,也就是下边讲的,对于一个无序序列反复筛选,就能得到一个堆。 第二个问题即堆的调整: 小根堆调整步骤: (1)输出堆顶元素之后,以堆中最后一个元素替代它,堆中最后一个元素即数组中最后一个元素,要时刻记得堆排序在实际编码中用的数据结构是数组,而不是树。 (2)将根结点值(注意这是上一步替代过的新值)与左、右子树的根节点值进行比较,并与其中较小者进行交换,左右子树根节点值即存储在数组索引?2i 和 2i + 1 位置上的元素。 (3)重复上述操作,直到叶子结点,将得到新的堆,这个从堆顶到叶子的调整过程叫做“筛选”。 大根堆的调整同理。 堆的建立首先明确一点,一个堆对应了一个完全二叉树,而完全二叉树中最后一个非叶子结点编号一定是n整除2,即我们在建堆的筛选操作的时候只需要从数组中索引为 n/2的结点开始筛选即可,需要注意的是建堆过程的筛选是自底向上的,即从最后一个非叶子结点进行筛选,一直筛选到根节点,这样才能保证整棵树是堆。在使用堆的时候,输出堆顶元素之后,把最后一个元素移动到堆顶之后,其实这时候只有以堆顶元素为根的树不是堆,因此需要对堆顶元素进行筛选,而以其它非叶子结点为根的树此时仍然是堆,因此只需要对堆顶元素的一次筛选过程就够了。 堆排序算法性能分析初始化堆时间复杂度是O(N),输出堆顶后调整堆的时间复杂度是O(logN),如果要实现堆排序,则要不断地输出堆顶,把N个元素都输出出去,每输出一次堆顶就需要一次调整(除了最后一次输出堆顶,这个时候就剩一个元素,不用调整),因此整个堆排序的时间复杂度是O(NlogN) 堆排序实例leetcode 215数组中第K大元素 215. 数组中的第K个最大元素 - 力扣(LeetCode) (leetcode-cn.com) 下面是大顶堆解法,因为是第k大,需要调整输出k-1个元素,并调整k-1次,每次调整的时间复杂度可认为是logN,因此时总的时间复杂度是KlogN
此外求第k大的数还可以用小顶堆来解,只需要维护一个大小为k的小顶堆,遍历数组中元素,如果比k大就替换堆顶,然后调整,如果比k小就直接遍历下一个,这样遍历完成之后的堆顶元素就是第k大的元素,时间复杂度NlogK 5 归并排序把两个或两个以上的有序子序列归并成一个有序序列,最常见的是2路归并排序。 归并排序 详解_k_koris的博客-CSDN博客_归并排序 主要是两个函数,一个用来分治,一个用来合并 分治函数一般用递归实现,递归出口是分治区间里只剩下了一个元素。 合并要负责将两个有序序列合并成一个有序序列,合并操作是自底向上的,即从两个区间都只有一个元素开始,直接比较大小合并成一个包含两个元素的有序序列。
最佳/最差/平均时间复杂度:O(N) 空间复杂度:临时数组O(N),递归栈 O(lgN),因此总的空间复杂度O(N) 6 桶排序非比较排序。 基数排序跟桶排序还是有一点区别,桶排序不是一种具体的排序方法,而是一种思想,基数排序和计数排序都可以算是用了桶排序的思想。基数排序的特点是多关键字。计数排序可参考马士兵说:计数排序,桶思想的一种_哔哩哔哩_bilibili,一般认为与频次有关的是计数排序的应用,比如下边的两道力扣题。 基数排序基本思想分为分配和收集两个步骤。 分配:设置若干个箱子,将关键字为k的记录放入第k个箱子(注意可能有些箱子直到最后都没放东西,即是空的)。 收集:将非空的箱子连接起来。 一般的排序只能针对一个关键字来排序,而基数排序可以综合多个关键字实现排序。 小例子: 这里的数字是三位数,我们可以认为有三个关键字(个位十位百位),因此需要进行三轮排序,我们按照从个位开始排序的方法,因为三个关键字的取值范围都是0到9,所以三轮排序时候用到的桶的数量都是10 第一轮: 第一趟分配 从数列中第一个数614开始按个位将其放到对应的桶里。 第一趟收集 可以看出经过第一趟分配与第一趟收集,所有的记录在第一个关键字即个位数上已经有序了。 第二轮: 第二轮是在第一轮的基础上进行的。 第二轮分配 从530开始按十位数将不同数字放到对应的桶里。 第二轮收集 第三轮: 在前两轮的基础上进行。 第三轮分配 从101开始按百位将数字放到对应的桶里。 第三轮收集 第三轮收集之后数列已经有序了,排序完成! 效率分析时间效率O(k*(n + m)) n:待排序序列中记录个数,比如上边例子中一共有10个数,n代表分配过程,有n个数我们就要进行n次比较然后将对应的数放到桶里。 m:关键字取值范围,即桶的个数,比如上边例子中因为数字范围0到9,因此有10个桶,收集的时候就要收集10个桶,m代表收集过程。 k:关键字个数,比如上边例子中一共有个位,十位,百位三个关键字,因此k=3,巧的是这三个关键字取值范围都一样,k代表要进行多少轮分配与收集。 空间效率O(n + m) n:代表收集过程,要收集回来n个记录。 m:代表桶,需要m个桶。 基数排序是稳定排序。 leetcode 347 前k个高频元素?时间复杂度是 O(N + K) 可以认为是只有一个关键字情况下的基数排序(对频次这个关键字进行一轮基数排序),也可以认为是计数排序的应用,毕竟跟频次有关。不如以后统称为桶排序吧。
leetcode 451 根据字符出现频率排序 计数排序的应用
7 内部排序算法总结内部排序是数据记录在内存中进行排序,外部排序是因为要排序的数据很多,内存不能一次性容纳,因此要访问外存,上边提到的排序算法都是内部排序算法。 桶排序,基数排序,计数排序是非比较式排序,其它都是基于比较的排序方法。 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/25 16:30:31- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |