在待排序的文件中,若存在多个关键字相同的记录,经过排序后这些具有相同关键字的记录之间的相对次序保持不变,该排序方法是稳定的;若具有相同关键字的记录之间的相对次序发生改变,则称这种排序方法是不稳定的。即所有相等的数经过某种排序方法后,仍能保持它们在排序之前的相对次序,则说这种排序算法是稳定的,反之,就是不稳定的。

稳定的排序算法如下表所示:

稳定的排序

时间复杂度

空间复杂度

冒泡排序(bubble sort)

最差、平均都是O(n^2),最好是O(n)

1

插入排序(insertion sort)

最差、平均都是O(n^2),最好是O(n)

1

归并排序(merge sort)

最差、平均、最好都是O(n log n)

O(n)

桶排序(bucket sort)

O(n)

O(k)

基数排序(Radix sort)

O(dn)(d是常数)

O(n)

二叉树排序(Binary tree sort)

O(n log n)

O(n)

不稳定的排序算法如下表所示:

不稳定的排序

时间复杂度

空间复杂度

选择排序(selection sort)

最差、平均都是O(n^2)

1

希尔排序(shell sort)

O(n log n)

1

堆排序(heapsort)

最差、平均、最好都是O(n log n)

1

快速排序(quicksort)

平均是O(n log n),最差是O(n^2)

O(log n)

一、冒泡排序

冒泡排序(BubbleSort)的基本概念是:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。至此第一趟结束,将最大的数放到了最后。在第二趟:仍从第一对数开始比较(因为可能由于第2个数和第3个数的交换,使得第1个数不再小于第2个数),将小数放前,大数放后,一直比较到倒数第二个数(倒数第一的位置上已经是最大的),第二趟结束,在倒数第二的位置上得到一个新的最大数(其实在整个数列中是第二大的数)。如此下去,重复以上过程,直至最终完成排序。

代码实现如下:

二、插入排序

插入排序的基本思想是每步将一个待排序的记录按其排序码值的大小,插到前面已经排好的文件中的适当位置,直到全部插入完为止。插入排序方法主要有直接插入排序和希尔排序。

直接插入排序具体算法描述如下:

1. 从第一个元素开始,该元素可以认为已经被排序

  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描

  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置

  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置

  5. 将新元素插入到下一位置中

  6. 重复步骤2

代码实现如下:

三、归并排序

归并排序是将两个或两个以上的有序子表合并成一个新的有序表。初始时,把含有n个结点的待排序序列看作由n个长度都为1的有序子表组成,将它们依次两两归并得到长度为2的若干有序子表,再对它们两两合并。直到得到长度为n的有序表,排序结束。将两个有序表合并和一个有序表称为二路归并。
    归并操作的工作原理如下:

  1、申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

  2、设定两个指针,最初位置分别为两个已经排序序列的起始位置

  3、比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

  4、重复步骤3直到某一指针达到序列尾

  5、将另一序列剩下的所有元素直接复制到合并序列尾

代码实现如下:

四、桶排序
    桶排序的基本思想就是把区间[0,1)划分成n个相同大小的子区间,或称桶,然后将n个输入数分布到各个桶中去。因为输入数均匀分布在[0,1)上,所以一般不会有很多数落在 一个桶中的情况。为得到结果,先对各个桶中的数进行排序,然后按次序把各桶中的元素列出来即可。

  在桶排序算法的代码中,假设输入是个含n个元素的数组A,且每个元素满足0≤ A[i]<1。另外还需要一个辅助数组B[O..n-1]来存放链表实现的桶,并假设可以用某种机制来维护这些表。

我的理解是:桶排序相当于一个N路的归并排序,首先将输入按均匀分布分到N个桶中,每一个桶都用一个链表来维护,并用插入排序对每个桶(也就是每一路)进行排序,最后将N个有序桶合并成一个,即得最终的排序结果。

伪码实现如下:

五、基数排序

设单关键字的每个分量的取值范围均是C0<=Kj<=Crd-1(0<=j<=rd),可能的取值个数rd称为基数.基数的选择和关键字的分解因关键字的类型而异.
  (1)若关键字是十进制整数,则按个、十等位进行分解,基数rd=10,C0=0,C9=9,d为最长整数的位数.
  (2)若关键字是小写的英文字符串,则rd=26,C0='a',C25='z',d为最长字符串的长度.
  基数排序的基本思想是:从低位到高位依次对待排序的关键码进行分配和收集,经过d趟分配和收集,就可以得到一个有序序列.

基数排序从低位到高位进行,使得最后一次计数排序完成后,数组有序。其原理在于对于待排序的数据,整体权重未知的情况下,先按权重小的因子排序,然后按权重大的因子排序。例如比较时间,先按日排序,再按月排序,最后按年排序,仅需排序三次。但是如果先排序高位就没这么简单了。基数排序源于老式穿孔机,排序器每次只能看到一个列,很多教科书上的基数排序都是对数值排序,数值的大小是已知的,与老式穿孔机不同。将数值按位拆分再排序,是无聊并自找麻烦的事。算法的目的是找到最佳解决问题的方案,而不是把简单的事搞的更复杂。基数排序更适合用于对时间、字符串等这些整体权值未知的数据进行排序。

我的理解是:基数排序算法中,数据可分解为d个因子,每个因子对排序结果都有影响(即权重),先按权重小的因子进行排序,后按权重大的因子进行排序,所有因子排序完即得结果。如时间可分解为三个因子:日、月、年,先按日对时间排序,再按月对时间排序,最后按年对时间进行排序,即可。注意:每一趟按因子进行的排序都必须是稳定的!

伪码实现如下:

六、二叉树排序

二叉排序树(Binary Sort Tree)又称二叉查找树。它或者是一棵空树;或者是具有下列性质的二叉树:

(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;

(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;

(3)左、右子树也分别为二叉排序树;

我的理解是:二叉树排序,即先建一个二叉排序树,然后中序遍历,即得到一个从小到大的排序结果。

插入结点:

1、首先执行查找算法,找出被插结点的父亲结点。

  2、判断被插结点是其父亲结点的左、右儿子。将被插结点作为叶子结点插入。

  3、若二叉树为空。则首先单独生成根结点。

  PS:新插入的结点总是叶子结点。依次插入数据即完成建树。

伪码实现如下:

删除结点:

将结点z从二叉排序树中删除,分三种情况讨论:

  1、如果结点z没有子女,则修改其父结点p[z],使NIL为其子女;

2、如果结点z只有一个子女,则可以通过在其子结点与父结点间建立一条链来删除z;

3、如果结点z只有两个子女,先删除z的后继y(它没有左子女),再用y的内容来替代z的内容。

PS:某一结点x的后继即具有大于key[x]中的关键字中最小者的那个结点,即中序遍历顺序下的后继

七、选择排序

选择排序的基本思想是每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。选择排序中主要使用直接选择排序和堆排序。

直接选择排序的过程是:首先在所有记录中选出序码最小的记录,把它与第1个记录交换,然后在其余的记录内选出排序码最小的记录,与第2个记录交换......依次类推,直到所有记录排完为止。

代码实现如下:


八、希尔排序

希尔(Shell)排序的基本思想是:先取一个正整数d1<n,把所有序号相隔d1的数组元素放一组,先在各组内进行直接插入排序;然后取第二个增量d2<d1重复上述的分组和排序,直至所取的增量di=1,即所有记录放在同一组中进行直接插入排序为止。该方法实质上是一种分组插入方法。
    一般取d1=n/2,di+1=di/2。如果结果为偶数,则加1,保证di为奇数。
    代码实现如下:

九、堆排序

堆的定义:n个关键字序列Kl,K2,…,Kn称为(Heap),当且仅当该序列满足如下性质(简称为堆性质):

  (1) ki≤K2i 且 ki≤K2i+1

或(2)Ki≥K2i 且 ki≥K2i+1(1≤i≤ n)

若将此序列所存储的向量R[1..n]看作是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。
    根结点(堆顶)的关键字是堆里所有结点关键字中最小者,称为小根堆;根结点的关键字是堆里所有结点关键字中最大者,称为大根堆。

用大根堆排序的基本思想如下:

  1、先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区

  2、再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key

  3、由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。

  ……

  直到无序区只有一个元素为止。

  伪码实现如下:

十、快速排序
    快速排序采用了一种分治的策略,通常称其为分治法,其基本思想是:将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的解组合为原问题的解。
    快速排序的具体过程如下:

  设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。

一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换;
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;
5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。

代码实现如下:

转载于:https://www.cnblogs.com/T8881/p/5211528.html

【数据结构】排序算法总结相关推荐

  1. 数据结构---排序算法的总结

    数据结构-排序算法的总结 分类 冒泡排序,时间复杂度O(n x n),空间复杂度O(1),稳定 简单选择排序,时间复杂度O(n x n),空间复杂度O(1),不稳定 希尔排序,时间复杂度O(n^1.3 ...

  2. 【数据结构排序算法系列】数据结构八大排序算法

    排序算法在计算机应用中随处可见,如Windows操作系统的文件管理中会自动对用户创建的文件按照一定的规则排序(这个规则用户可以自定义,默认按照文件名排序)因此熟练掌握各种排序算法是非常重要的,本博客将 ...

  3. 数据结构-排序算法总结与感悟

    数据结构-排序算法总结 一,排序的基本概念 排序:有n个记录的序列{R1,R2,-,Rn},其相应关键字的序列是{K1,K2, -,Kn },相应的下标序列为1,2,-, n.通过排序,要求找出当前下 ...

  4. C++基础-介绍·数据结构·排序·算法

    C++基础-介绍·数据结构·排序·算法 特点 使用方向 RPC Data Struct 数据结构 栈 Stack 内存分配中的栈 队列 List 数组 Array 链表 LinkTable 树 Tre ...

  5. 数据结构-排序算法(c语言实现篇)

    数据结构-排序算法(c语言实现篇) 排序算法是非常常用的算法,从介绍排序的基本概念,到介绍各种排序算法的思想.实现方式以及效率分析.最后比较各种算法的优劣性和稳定性. 1 排序的概念及应用 1.1 排 ...

  6. 数据结构排序算法实验报告_数据结构与算法-堆排序

    堆排序 堆排序是指利用堆这种数据结构所设计的一种排序算法.堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子节点的键值或索引总是小于(或者大于)它的父节点,堆排序的时间复杂度为O(nlogn).( ...

  7. 选择排序稳定吗_最常见的四种数据结构排序算法你不知道?年末怎么跳槽涨薪...

    前言 在学习数据结构的时候必然接触过排序算法,而且在日常开发中相信排序算法用得也比较多.而排序算法众多,各个效率又不同,难以记住.小编今天会介绍一些常用排序算法的特点和实现,对比不同排序算法的效率. ...

  8. 数据结构排序算法实验报告_[数据结构与算法系列]排序算法(二)

    我的上一篇文章向大家介绍了排序算法中的冒泡排序.插入排序和选择排序.它们都是平均时间复杂度为 O(n^2) 的排序算法,同时还为大家讲解了什么是原地排序和什么是排序的稳定性.下图是这三种算法的比较,不 ...

  9. 数据结构——排序算法(含动态图片)

    目录 插入排序 交换排序 选择排序 归并排序 常用排序算法复杂度和稳定性总结 前言 排序是<数据结构>中最基本的学习内容.排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行 ...

  10. 数据结构—排序算法总结(插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、合并排序、计数排序)

    *排序 所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作. 稳定性 在待排序的数组中,如果出现多个相同的关键字,例如:98751555512,中出现重复的数字,在 ...

最新文章

  1. 如何做网络推广浅析在网站优化中如更换域名该如何避免降权风险?
  2. php代码编辑器6,优秀的PHP代码编辑器_Blumentals Rapid PHP V13.6 免费版
  3. Android(java)学习笔记27:TextView属性大全
  4. MiniO 磁盘缓存快速入门
  5. 【HDU 2222】Keywords Search
  6. 布谷鸟沙盒分析静态文件_【虚拟机镜像分析】
  7. python Selenium调用 IE/GoogleChrome/Firefox浏览器驱动的方法及下载地址
  8. 如何为编程爱好者设计一款好玩的智能硬件(十)——无线2.4G通信模块研究·一篇说完...
  9. 在VS2019 C++ 中实现Socket通信,添加ws2_32.lib静态库
  10. 分享200个App移动端模板
  11. 3GPP协议下载教程
  12. Zookeeper 客户端之 Curator
  13. 什么是软件维护?在软件已经交付使用后,为了改正错误或满足新需求而修改软件的过程。它有哪几种类型?
  14. php程序如何删除文件夹和文件
  15. HTTPS访问Git远程仓库,上报SSL证书错误解决方法
  16. python-pptx替换ppt模板的文本
  17. 微信支付API3 APP【统一下单 APIV3】
  18. 【转发】JS实现密码加密
  19. 《赢在中国》精彩评语
  20. Android向通讯录添加联系人的一般方法

热门文章

  1. python 发邮件-带附件-文本-html
  2. 【AGC014E】Blue and Red Tree
  3. vue 使用font-awesome 只需两步
  4. 相机位姿估计2:[应用]实时位姿估计与三维重建相机姿态
  5. 《构建之法》阅读笔记07
  6. Ember——在构建Ember应用程序时,我们会使用到六个主要部件:应用程序(Application)、模型(Model)、视图(View)、模板(Template)、路由(...
  7. Android调用前置摄像头的方法
  8. Jetty Groovy AppServer
  9. Android 线程死锁的案例
  10. 如果看懂git -help