四种简单的排序算法
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1.插入排序
算法思想
插入排序使用了两层嵌套循环,逐个处理待排序的记录。每个记录与前面已经排好序的记 录序列进行比较, 并将其插入到合适的位置。 假设数组长度为 n, 外层循环控制变量 i 由 1 至 n-1 依次递进,用于选择当前处理哪条记录;里层循环控制变量 j,初始值为 i,并由 i 至 1 递减, 与上一记录进行对比,决定将该元素插入到哪一个位置。这里的关键思想是,当处理第 i 条记 录时,前面 i-1 条记录已经是有序的了。需要注意的是,因为是将当前记录与相邻的上一记录 相比较,所以循环控制变量的起始值为 1(数组下标),如果为 0 的话,上一记录为-1,则数组 越界。 现在我们考察一下第 i 条记录的处理情况:假设外层循环递进到第 i 条记录,设其关键码 的值为 X,那么此时有可能有两种情况: 1. 如果上一记录比 X 大, 那么就交换它们, 直到上一记录的关键码比 X 小或者相等为止。 2. 如果上一记录比 X 小或者相等,那么之前的所有记录一定是有序的,且都比 X 小,此 时退出里层循环。外层循环向前递进,处理下一条记录。
四种简单的排序算法
我觉得如果想成为一名优秀的开发者,不仅要积极学习时下流行的新技术,比如 WCF、 Asp.Net MVC、AJAX 等,熟练应用一些已经比较成熟的技术,比如 Asp.Net、WinForm。还应该有 着牢固的计算机基础知识,比如数据结构、操作系统、编译原理、网络与数据通信等。有的朋友 可能觉得这方面的东西过于艰深和理论化,望而却步,但我觉得假日里花上一个下午的时间,研 究一种算法或者一种数据结构,然后写写心得,难道不是一件乐事么?所以,我打算将一些常见 的数据结构和算法总结一下, 不一定要集中一段时间花费很大精力, 只是在比较空闲的时间用一 种很放松的心态去完成。我最不愿意的,就是将写博客或者是学习技术变为一项工作或者负担, 应该将它们视为生活中的一种消遣。 人们总是说坚持不易, 实际上当你提到 “坚持” 两个字之时, 说明你已经将这件事视为了一种痛苦,你的内心深处并不愿意做这件事,所以才需要坚持。你从 不曾听人说“我坚持玩了十年的电子游戏”,或者“坚持看了十年动漫、电影”、“坚持和心爱 的女友相处了十年”吧?我从来不曾坚持,因为我将其视为一个爱好和消遣,就像许多人玩网络 游戏一样。 好了,闲话就说这么多吧,我们回到正题。因为这方面的著作很多,所以这里只给出简单 的描述和实现,供我本人及感兴趣的朋友参考。我会尽量用 C#和 C++两种语言实现,对于一些不 好用 C#表达的结构,仅用 C++实现。 本文将描述四种最简单的排序方法,插入排序、泡沫排序、选择排序、希尔排序,我在这 里将其称为“简单排序”,是因为它们相对于快速排序、归并排序、堆排序、分配排序、基数排 序从理解和算法上要简单一些。对于后面这几种排序,我将其称为“高级排序”。
上面 Console.WriteLine()方法和 AlgorithmHelper.PrintArray()方法仅仅是出于测试方 便,PrintArray()方法依次打印了数组的内容。swap<T>()方法则用于交换数组中的两条记录, 也对交换数进行了打印(这里我注释掉了,但在测试时可以取消对它们的注释)。外层 for 循环 控制变量 i 表示当前处理第 i 条记录。 public class AlgorithmHelper { // 打印数组内容 public static void PrintArray<T>(T[] array) { Console.Write(" Array:"); foreach (T item in array) { Console.Write(" {0}", item); } Console.WriteLine(); } } // 获得 Comparer,进行比较 public class ComparerFactory { public static IComparer<int> GetIntComparer() { return new IntComparer(); } public class IntComparer : IComparer<int> { public int Compare(int x, int y) { return x.CompareTo(y); } } } 上面这段代码我们创建了一个 ComparerFactory 类,它用于获得一个 IntComparer 对象, 这个对象实现了 IComparer<T>接口,规定了两个 int 类型的关键码之间比较大小的规则。如果 你有自定义的类型,比如叫 MyType,只需要在 ComparerFactory 中再添加一个类,比如叫 MyTypeComparer,然后让这个类也实现 IComparer<T>接口,最后再添加一个方法返回 MyTypeComparer 就可以了。
输出演示(C#)
static void Main(string[] args) { int[] array = {42,20,17,13,28,14,23,15}; AlgorithmHelper.PrintArray(array); SortAlgorithm.BubbleSort (array, ComparerFactory.GetIntComparer()); }
Βιβλιοθήκη Baidu
算法实现(C#)
public class SortAlgorithm { // 插入排序 public static void InsertSort<T, C>(T[] array, C comparer) where C:IComparer<T> { for (int i = 1; i <= array.Length - 1; i++) { //Console.Write("{0}: ", i); int j = i; while (j>=1 && comparer.Compare(array[j], array[j - 1]) < 0) { swap(ref array[j], ref array[j-1]); j--; } //Console.WriteLine(); //AlgorithmHelper.PrintArray(array); } } // 交换数组 array 中第 i 个元素和第 j 个元素 private static void swap<T>(ref T x,ref T y) { // Console.Write("{0}<-->{1} ", x, y); T temp = x; x = y; y = temp; } }
算法实现(C++)
// 冒泡排序 template <class T, class C> void BubbleSort(T a[], int length){ for(int i=0;i<=length-2;i++){ for(int j=length-1; j>=1; j--){ if(C::Smaller(a[j], a[j-1])) swap(a[j], a[j-1]); } } }
} }
2.冒泡排序
算法思想
如果你从没有学习过有关算法方面的知识,而需要设计一个数组排序的算法,那么很有可 能设计出的就是泡沫排序算法了。因为它很好理解,实现起来也很简单。它也含有两层循环,假 设数组长度为 n,外层循环控制变量 i 由 0 到 n-2 递增,这个外层循环并不是处理某个记录,只 是控制比较的趟数,由 0 到 n-2,一共比较 n-1 趟。为什么 n 个记录只需要比较 n-1 趟?我们可 以先看下最简单的两个数排序:比如 4 和 3,我们只要比较一趟,就可以得出 3、4。对于更多的 记录可以类推。 数组记录的交换由里层循环来完成, 控制变量 j 初始值为 n-1 (数组下标) , 一直递减到 1。 数组记录从数组的末尾开始与相邻的上一个记录相比, 如果上一记录比当前记录的关键码大, 则 进行交换,直到当前记录的下标为 1 为止(此时上一记录的下标为 0)。整个过程就好像一个气 泡从底部向上升,于是这个排序算法也就被命名为了冒泡排序。 我们来对它进行一个考察,按照这种排序方式,在进行完第一趟循环之后,最小的一定位 于数组最顶部(下标为 0);第二趟循环之后,次小的记录位于数组第二(下标为 1)的位置; 依次类推,第 n-1 趟循环之后,第 n-1 小的记录位于数组第 n-1(下标为 n-2)的位置。此时无 需再进行第 n 趟循环,因为最后一个已经位于数组末尾(下标为 n-1)位置了。
输出演示(C#)
接下来我们看一下客户端代码和输出: static void Main(string[] args) { int[] array = {42,20,17,13,28,14,23,15}; //int[] array = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; AlgorithmHelper.PrintArray(array); SortAlgorithm.InsertSort
3.选择排序
算法思想
选择排序是对冒泡排序的一个改进,从上面冒泡排序的输出可以看出,在第一趟时,为了 将最小的值 13 由数组末尾冒泡的数组下标为 0 的第一个位置,进行了多次交换。对于后续的每 一趟,都会进行类似的交换。 选择排序的思路是:对于第一趟,搜索整个数组,寻找出最小的,然后放置在数组的 0 号 位置;对于第二趟,搜索数组的 n-1 个记录,寻找出最小的(对于整个数组来说则是次小的), 然后放置到数组的第 1 号位置。在第 i 趟时,搜索数组的 n-i+1 个记录,寻找最小的记录(对于 整个数组来说则是第 i 小的),然后放在数组 i-1 的位置(注意数组以 0 起始)。可以看出,选 择排序显著的减少了交换的次数。 需要注意的地方是:在第 i 趟时,内层循环并不需要递减到 1 的位置,只要循环到与 i 相 同就可以了, 因为之前的位置一定都比它小 (也就是第 i 小) 。 另外里层循环是 j>i, 而不是 j>=i, 这是因为 i 在进入循环之后就被立即保存到了 lowestIndex 中。
简单排序
开始之前先声明一个约定,对于数组中保存的数据,统一称为记录,以避免和“元素”, “对象”等名称相混淆。对于一个记录,用于排序的码,称为关键码。很显然,关键码的选择与 数组中记录的类型密切相关,如果记录为 int 值,则关键码就是本身;如果记录是自定义对象, 它很可能包含了多个字段,那么选定这些字段之一为关键码。凡是有关排序和查找的算法,就会 关系到两个记录比较大小,而如何决定两个对象的大小,应该由算法程序的客户端(客户对象) 决定。对于.NET 来说,我们可以创建一个实现了 IComparer<T>的类(对于 C++也是类似)。关 于 IComparer<T>的更多信息,可以参考这篇文章《基于业务对象的排序》。最后,为了使程序 简单,对于数组为空的情况我并没有做处理。
算法实现(C#)
// 泡沫排序 public static void BubbleSort<T, C>(T[] array, C comparer) where C : IComparer<T> { int length = array.Length; for (int i = 0; i <= length - 2; i++) { //Console.Write("{0}: ", i + 1); for (int j = length - 1; j >= 1; j--) { if (comparer.Compare(array[j], array[j - 1]) < 0) { swap(ref array[j], ref array[j - 1]); } } //Console.WriteLine(); //AlgorithmHelper.PrintArray(array); } }
(array, ComparerFactory.GetIntComparer()); }
算法实现(C++)
// 对 int 类型进行排序 class IntComparer{ public: static bool Smaller(int x, int y){ return x<y; } static bool Equal(int x, int y){ return x==y; } static bool Larger(int x, int y){ return x>y; } }; // 插入排序 template <class T, class C> void InsertSort(T a[], int length){ for(int i=1;i<=length-1;i++){ int j = i; while(j>=1 && C::Smaller(a[j], a[j-1])){ swap(a[j], a[j-1]); j--; }