“《算法》第4版第2章‘排序’”:初级排序算法(选择、冒泡、插入、希尔)

  《算法》第4版作者是Robert Sedgewick 和 Kevin Wayne。

 1. 选择排序

  选择排序可以说是最简单的排序方法。首先,找到数组中最小的那个元素;其次,将它与数组的第一个元素交换位置(如果第一个元素就是最小元素,那么它就和自己交换);再次,在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。如此往复,直到将整个数组排序。

  该书中提出一个命题:对于长度为N的数组,选择排序需要大约N2/2次比较和N次交换。

   程序如下:

 1 void SelectionSort::sort()
 2 {
 3     // 将a[i]和a[i+1..len]中最小的元素交换
 4     for (int i = 0; i < len; i++)
 5     {
 6         int min = i;
 7         for (int j = i + 1; j < len; j++)
 8         {
 9             if (less(arr[j], arr[min]))
10             {
11                 min = j;
12             }
13         }
14         exchange(i, min);
15     }
16 }

 2. 冒泡排序

  从第一个元素开始,将相邻的元素两两进行比较,按照从小到大或者从大到小的顺序进行交换,这样一趟过去后,最大或最小的数字被交换到了最后一位;然后,再从头开始进行两两比较交换,直到倒数第二位时结束;以此类推,一直到所有元素都有序为止。一个例子如下:

  原始待排序数组| 6 | 2 | 4 | 1 | 5 | 9 |

  第一趟排序(外循环)

  第一次两两比较6 > 2交换(内循环)

  交换前状态| 6 | 2 | 4 | 1 | 5 | 9 |

  交换后状态| 2 | 6 | 4 | 1 | 5 | 9 |

  第二次两两比较,6 > 4交换

  交换前状态| 2 | 6 | 4 | 1 | 5 | 9 |

  交换后状态| 2 | 4 | 6 | 1 | 5 | 9 |

  第三次两两比较,6 > 1交换

  交换前状态| 2 | 4 | 6 | 1 | 5 | 9 |

  交换后状态| 2 | 4 | 1 | 6 | 5 | 9 |

  第四次两两比较,6 > 5交换

  交换前状态| 2 | 4 | 1 | 6 | 5 | 9 |

  交换后状态| 2 | 4 | 1 | 5 | 6 | 9 |

  第五次两两比较,6 < 9不交换

  交换前状态| 2 | 4 | 1 | 5 | 6 | 9 |

  交换后状态| 2 | 4 | 1 | 5 | 6 | 9 |

  第二趟排序(外循环)

  第一次两两比较2 < 4不交换

  交换前状态| 2 | 4 | 1 | 5 | 6 | 9 |

  交换后状态| 2 | 4 | 1 | 5 | 6 | 9 |

  第二次两两比较,4 > 1交换

  交换前状态| 2 | 4 | 1 | 5 | 6 | 9 | 
  交换后状态| 2 | 1 | 4 | 5 | 6 | 9 |

  第三次两两比较,4 < 5不交换

  交换前状态| 2 | 1 | 4 | 5 | 6 | 9 | 
  交换后状态| 2 | 1 | 4 | 5 | 6 | 9 |

  第四次两两比较,5 < 6不交换

  交换前状态| 2 | 1 | 4 | 5 | 6 | 9 |

  交换后状态| 2 | 1 | 4 | 5 | 6 | 9 |

  第三趟排序(外循环)

  第一次两两比较2 > 1交换

  交换后状态| 2 | 1 | 4 | 5 | 6 | 9 |

  交换后状态| 1 | 2 | 4 | 5 | 6 | 9 |

  第二次两两比较,2 < 4不交换

  交换后状态| 1 | 2 | 4 | 5 | 6 | 9 | 
  交换后状态| 1 | 2 | 4 | 5 | 6 | 9 |

  第三次两两比较,4 < 5不交换

  交换后状态| 1 | 2 | 4 | 5 | 6 | 9 | 
  交换后状态| 1 | 2 | 4 | 5 | 6 | 9 |

  第四趟排序(外循环)无交换

  第五趟排序(外循环)无交换

 

  排序完毕,输出最终结果1 2 4 5 6 9

 1 void BubbleSort::sort()
 2 {
 3     // 一一比较a[j]和a[j+1]并当a[j]>a[j+1]时进行元素交换
 4     for (int i = len - 1; i > -1; i--)
 5     {
 6         for (int j = 0; j < i; j++)
 7         {
 8             if (!less(arr[j], arr[j+1]))
 9             {
10                 exchange(j, j + 1);
11             }
12         }
13     }
14 }

 3. 插入排序

  插入排序就是每一步都将一个待排数据按其大小插入到已经排序的数据中的适当位置,直到全部插入完毕。 插入排序方法分直接插入排序和折半插入排序两种,这里只介绍直接插入排序。一个例子如下图:
  

  该书中提出一个命题:对于随机排列的长度为N且主键不重复的数组,平均情况下插入排序需要大约N2/4次比较及大约N2/4次交换。最坏情况下需要大约N2/2次比较和大约N2/2次交换,最好情况下需要N-1次比较和0次交换。 

  代码如下:

 1 void InsertionSort::sort()
 2 {
 3     for (int i = 1; i < len; i++)
 4     {
 5         // 将a[i]插入到a[i-1]、a[i-2]、a[i-3]...之中
 6         for (int j = i; j > 0 && less(arr[j], arr[j - 1]);j--)
 7         {
 8             exchange(j, j - 1);
 9         }
10     }
11 }

 4. 希尔排序

  下边描述摘自一博文

  希尔排序的实质就是分组插入排序,该方法又称缩小增量排序,因DL.Shell于1959年提出而得名。

  该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。

   以n=10的一个数组49, 38, 65, 97, 26, 13, 27, 49, 55, 4为例

   第一次 gap = 10 / 2 = 5

   49   38   65   97   26   13   27   49   55    4

   1A    |     |      |     |      1B   |    |   |   |

   2A    |      |     |             2B    |    |     |

3A     |     |                   3B |    |

4A    |                         4B    |

5A                             5B

   1A、1B、2A、2B等为分组标记,数字相同的表示在同一组,大写字母表示是该组的第几个元素, 每次对同一组的数据进行直接插入排序。即分成了五组(49, 13) (38, 27) (65, 49)  (97, 55)  (26, 4),这样每组排序后就变成了(13, 49)  (27, 38)  (49, 65)  (55, 97)  (4, 26),下同。

   第二次 gap = 5 / 2 = 2

   排序后

   13   27   49   55   4    49   38   65   97   26

   1A    |    1B    |    1C    |    1D    |    1E    |

2A         2B          2C         2D         2E

   第三次 gap = 2 / 2 = 1

   4    26   13   27   38    49   49   55   97   65

   1A  1B   1C   1D   1E   1F   1G   1H   1I    1J

   第四次 gap = 1 / 2 = 0 排序完成得到数组:

   4   13   26   27   38    49   49   55   65   97

  代码如下:

 1 void ShellSort::sort()
 2 {
 3     for (int step = len / 2; step > 0; step /= 2)
 4     {
 5         for (int i = step; i < len; i++)
 6         {
 7             // 将a[i]插入到a[i-step]、a[i-2*step]、a[i-3*step]...之中
 8             for (int k = i; k >= step && less(arr[k], arr[k - step]); k -= step)
 9             {
10                 exchange(k, k - step);
11             }
12         }
13     }
14 }

  完整代码请参考Github.

时间: 2025-01-14 16:45:53

“《算法》第4版第2章‘排序’”:初级排序算法(选择、冒泡、插入、希尔)的相关文章

排序-初级排序

http://algs4.cs.princeton.edu/21elementary/ 排序算法分析:比较(Compare),交换(Exchange),如果没有使用 选择排序:首先,找到最小的元素然后和第一个数做交换,然后在剩下的元素里找最小的,和第二个数做交换,一直下去. 选择排序用了: ~N2/2 次比较(compares)and N 次 交换(exchanges) 插入排序:按先后顺序取出一个数,将其插入到已经有序的队列中,然后再去下个,扑克牌就是这样. 插入排序: 平均 ~N2/4 co

算法导论学习笔记——第8章 线性时间排序

任意一种比较排序算法,在最坏情况下的运行时间下限是Ω(nlgn) 计数排序 假设n个输入元素中的每一个都是介于0到k之间的整数,k为某个整数,当k=O(n)时,计数排序的运行时间为Θ(n) 1 //输入数组A[1..n],存放排序结果数组B[1..n],临时存储区C[0..k] 2 COUNTING-SORT(A,B,k) 3 for i←0 to k 4 do C[i]←0 5 for j←1 to length[A] 6 do C[A[j]]←C[A[j]]+1 7 for i←1 to k

《算法导论》读书笔记--第二章 2.2 分析算法

2.2分析算法 分析算法的结果意味着预测算法需要的资源.虽然有时候关心内存.通讯或者计算机硬件,但是通常我们想度量的是时间. 在分析算法之前,要有一个实现技术的模型,包括描述所用资源及其代价的模型.我们假定一种通用的单处理器计算模型—随机访问机(random-access machine,RAM)来作为我们的实现技术,算法可以用计算机程序来实现.在RAM模型中,指令一条接一条执行,并没有并发操作.RAM模型包含真是计算机中常见的指令:算术指令(如加法.减法.乘法.取余.向上取整.向下取整).数据

《算法导论》读书笔记--第二章 2.3 设计算法

我们可以使用的算法设计技术有很多.插入排序用的是增量方法,即在已经排好的数组中不断加入新的元素.下面考虑一种被称为"分治法"的设计方法. 2.3.1分治法 分治法的思想:将原问题分解为几个规模较小但是类似于原问题的子问题,递归地求解这些子问题,然后合并这些子问题的解来建立原问题的解.分治模式在每层递归时有三个步骤: 分解原问题为若干子问题: 解决这些子问题,递归地求解各子问题,若子问题规模足够小,则直接求解: 合并这些子问题的解成原问题的解. 归并排序算法完全遵循分治模式,操作如下:

冒泡---插入---希尔排序

<span style="color:#ff0000;">#include<iostream> using namespace std; #define MAXSIZE 21 typedef int SqList[MAXSIZE]; #define ElementType int void Swap(int &a, int &b) { a = a^b; b = a^b; a = a^b; } ///////////////////////////

算法(第四版)学习笔记之java实现选择排序

选择排序步骤: 1.找到数组中参与遍历比较的所有元素中的最小元素的下标: 2.将最小元素与数组中参与遍历比较的第一个元素进行交换(如果第一个元素就是最小元素的话,那么也会进行一次交换): 3.若数组中还有需要参与遍历比较的元素,则跳转到步骤1:否则排序结束. 在算法第四版中给出的所有排序均是适用于任意实现了Comparable接口的数据类型,若要将数字作为测试用例,请勿使用基本数据类型,改用Integer等实现了Comparable接口的对象. 选择排序代码如下: /** * * @author

深入理解排序算法(一):初级排序算法

[本系列博文会对常见的排序算法进行分析与总结,并会在最后提供几道相关的一线互联网企业面试/笔试题来巩固所学及帮助我们查漏补缺.项目地址:https://github.com/absfree/Algo.由于个人水平有限,叙述中难免存在不清晰准确的地方,希望大家可以指正,谢谢大家:)] 一.概述 我们在日常开发中经常需要对一组数据对象进行排序,这里的数据对象不仅包括数字,还可能是字符串等抽象数据类型(Abstract Data Type).由于排序是很多其他操作(比如二分查找)能够高效进行的基础,因

“《算法》第4版第2章‘排序’”:归并排序

归并排序(Merge Sort,台湾译作:合并排序)是建立在归并操作上的一种有效的排序算法.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用. 归并操作(Merge),也叫归并算法,指的是将两个已经排序的序列合并成一个序列的操作.归并排序算法依赖归并操作.归并排序有多路归并排序.两路归并排序 , 可用于内排序,也可以用于外排序.这里仅对内排序的两路归并方法进行讨论. 归并排序的步骤如下: 1)Divide: 将待排序序列(原问题)分成两个规模大致相等的子序列(子问题

10-6-起泡排序-内部排序-第10章-《数据结构》课本源码-严蔚敏吴伟民版

课本源码部分 第10章  内部排序 - 起泡排序 ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接??? <数据结构-C语言版>(严蔚敏,吴伟民版)课本源码+习题集解析使用说明        课本源码合辑  链接??? <数据结构>课本源码合辑        习题集全解析  链接??? <数据结构题集>习题解析合辑        本源码引入的文件  链接? SequenceListType.c        相关测试数据下载  链接?

【JavaScript】【算法】JavaScript版排序算法

JavaScript版排序算法:冒泡排序.快速排序.插入排序.希尔排序(小数据时,希尔排序会比快排快哦) 1 //排序算法 2 window.onload = function(){ 3 var array = [0,1,2,44,4, 4 324,5,65,6,6, 5 34,4,5,6,2, 6 43,5,6,62,43, 7 5,1,4,51,56, 8 76,7,7,2,1, 9 45,4,6,7,8]; 10 //var array = [4,2,5,1,0,3]; 11 array