本篇博客中的代码实现依然采用Swift3.0来实现。在前几篇博客连续的介绍了关于查找的相关内容, 大约包括线性数据结构的顺序查找、折半查找、插值查找、Fibonacci查找,还包括数结构的二叉排序树以及平衡二叉树的构建与查找,然后还聊了哈希表的构建与查找。接下来的几篇博客中我们就集中的聊一下常见的集中排序方式,并并给出相应的时间复杂度。本篇博客我们将会详细的介绍 冒泡排序、插入排序、希尔排序以及选择排序,下篇博客将继续介绍堆排序、归并排序以及快速排序的相关内容。当然上述内容的代码实现我们依然采用Swift面向对象语 言来实现。
本篇博客的思路与以往博客的思路一直,先分析每种排序的规则,然后给出原理示意图,最后根据示意图给出相应的代码实现。编程这东西,只要是思路清晰,给出相应的代码实现并不困难,本篇是使用Swift语言来实现的,如果你对Swift语言不熟悉,你可以选择其他你熟悉的语言来实现。虽然语言不同,但是思路和方法都是一样的。废话少说,开始今天博客的主题。
一、排序协议的定义
在博客的开头的,我们先给出排序协议的定义。因为我们本篇博客含有多种排序方式,为了使每种排序方法对外调用方式一致,我们需要定义一个排序的相关协议。所有排序的相关类都必须遵循该协议,让此协议来定义具体的排序类对外的调用方式。
下方的SortType协议就是我们定义的排序类型的协议。其中的sort()方法是遵循该协议的类必须要实现的方法。sort()函数的参数是一个含有Int类型的数组,该数组就是要排序的数组。该方法的返回值是已经被排好序的数组。具体代码如下所示。
二、冒泡排序
接下来我们来聊一下冒泡排序,冒泡排序就像其名字一样,还是比较生动的。在冒泡排序过程中会将数组分成两部分,一部分是已经有序的数列,一部分是无序的数列。无序数列中不断的将其中最小的值往有序序列中冒泡,泡冒完后,我们的序列就创建好了。本部分,我们将要给出冒泡排序的示意图,已经相应的代码实现。
1、冒泡排序示意图
下方就是第一轮冒泡的具体过程,我们要对[62, 88, 58, 47, 62, 35, 73, 51, 99, 37, 93]序列进行冒泡排序。经过第一轮的冒泡后,该序列中最小的值35被冒到了数列的最前方。因为冒泡的过程是挨个比较已经交换的过程。元素状态我们的泡中是93,93与前一个值37进行比较,发现37要小于93,所以将泡中的值改成37,并往前移动。紧接着37在与前面的99比较,发现泡中的值要小,此刻不更新泡中的值并往前移动一个格。以此类推,无序序列中最小的值就会被冒到序列的起始位置。
每轮冒泡都会从无序序列中冒出那个最小的值,所以经过n(数列有n个值)次冒泡后,我们的数列就是有序的了。冒泡过程中的比较与交换的具体步骤如下所示:
2.代码实现
根据上述示意图,我们很容易给出下方的代码。下方就是冒泡排序的代码,BubbleSort这个类就是冒泡排序所对应的类,此类为了统一对外的调用方式,所以必须要遵循我们上一部分所定义的SortType协议。
说白了,冒泡的过程就是不断比较和交换的过程,如果前一个值比后一个值要大,那么就要进行交换了
3、运行结果
下方代码就是上述BubbleSort类所运行的具体结果。排序结果中详细的打印了冒泡排序在每一轮冒泡中每一步要做的事情,具体如下所示。
三、插入排序
插入排序算是比较好理解的排序方式,插入排序也是将要排序的数列分为两部分,前半部分是已经排好序的,后半部分则是无序的。插入排序中的插入是指“取出无序数列中第一个值,插入到有序数列中相应的位置”。其实这个插入过程也是不断比较和交换的过程。
1、插入排序示意图
下方就是插入排序的示意图,红色部分是有序数列,而绿色部分是无序数列。每一轮插入都会取出无序数列中的第一个元素插入到有序数列中,这个插入的过程其实就是一个比较交换的过程,如果要插入的值比前面的值要小,就要交换,直到不能交换为止。下方就是插入排序的过程。具体如下所示:
2、代码实现
有了上述的示意图,给出相应的代码实现并不困难。代码的核心思想就是通过循序不断从无序数列中取出值,然后循环遍历有序数列寻找合适的插入点。在下方中有两个循环嵌套,外层循环负责不断从无序序列中取值,然后通过内层循环将外层循环取出的值插入到有序数列中相应的位置,具体如下代码所示:
3、运行结果
下方是运行结果的截图,该运行结果其实就是插入排序的详细过程。每一轮插入的过程就是有序序列增加,无序序列减少的过程。下方就是插入过程的详细信息。
四、希尔排序
因为这个排序是一个叫希尔的人发明的,所以就叫希尔排序了。其实希尔排序是插入排序的升级版, 希尔排序根据其排序的特点又叫做缩小增量排序。希尔排序的大体步骤就是先将无序序列按照一定的步长(增量)分为几组,分别将这几组中的数据通过插入排序的方式将其进行排序。然后缩小步长(增量)分组,然后将组内的数据再次进行排序。知道增量为1位置。经过上述这些步骤,我们的序列就是有序的了。其实上述的插入排序就是增量为1的希尔排序,下方会给出相应的示意图以及代码实现。
1.希尔排序示意图
下方就是希尔排序的详细步骤,接下来我们将会对每一步进行详细的解说。如下所示:
(1)、首先按照增量进行分组,因为我们要排序的数列有11个,增量初始值是step = 11 / 2 = 5。也就是按照增量为5的步长对数组进行分组。在下方第一步中就是按照增量为5的方式进行分组的。我们将为一组的元素使用直线进行相连,分完组后,我们就将组内中的元素进行插入排序。
(2)、将上一步使用的增量进行缩小,也就是本步骤的step = 5 / 2 = 2。 本部分,就要按照2的增量将上一步排序后的数组进行分组,然后再次将每个组内的数据进行插入排序。
(3)、再次缩小增量,此刻step = 2 / 2 = 1, 当增量为1时,其实就是我们上一部分的插入排序。将整个数组进行插入排序,然后我们的数组就是有序的了。
具体示意图如下所示:
2、希尔排序的代码实现
根据上述的步骤,然后再结合着插入排序的代码,给出希尔排序相应的代码并不困难。就是将插入排序的步长从1修改成我们每次生成的步长即可,每次增量排序完毕后,我们要对增量按照相应的规则进行缩小即可。下方就是希尔排序的代码实现。
在下方代码中,最外层循环负责增量的生成和缩减,里边的双重循环就是我们之前我们插入排序的代码,不步长要使用我们希尔排序生成的step,具体代码如下所示:
3、运行结果
下方就是Shell排序的运行结果,从下方结果中我们不难看出增量是逐渐减小的。下方的输出结果结合着本部分第一部分的示意图看更为直观一些。
五、选择排序
接下来来聊聊选择排序,选择排序也是比较好理解的。在选择排序过程中,数组仍然被分作有序和无序两部分。而选择排序中的“选择”是指不断从无序序列中选择最小的值放入到有序序列的最后的位置,换句话说就是从现有的无序序列中找出那个最小的值,然后与无序序列的第一个值进行交换,然后缩小无序序列的范围即可。因为有序序列的最后一个值与无序序列的第一个值紧挨着,交换后,这个无序序列中的第一个值就成了有序序列的最后一个值。重复这个选择的过程,我们的数组就会变得有序。下方将会给出详细的示意图以及相应的代码实现。
1.选择排序示意图
下方就是简单选择排序的部分步骤,只需要重复下方的步骤就可以通过选择排序将我们的数组变成有序的序列。下方是对下方步骤的详细介绍:
初识状态下,我们整个数组就是无序的,从整个数组中我们找到了最小的元素35,其下标为5。然后将35与无序序列第一个元素62进行交换。交换后,有序序列中就有了一个值:35,而无序序列中就少了一个值:35。无序序列中的第一个值也就是变成了88。
再次从无序序列中选择那个最小的值。于是乎我们又找到了37,然后让37与88进行交换。有序序列就成了{35,37}。
再次从无序序列中选择最小的那个值,经过查找我们找到了47,然后将47与58进行交换。此刻有序序列就成了{35, 37, 47}。
重复的从无序序列中选择最小的值进行交换……
2.选择排序具体代码实现
有了上述选择的示意图,根据思路敲代码。下方就是选择排序的具体代码实现。代码实现起来还是比较简单的,就是通过一个循环,不断的从无序序列中选出那个最小的值与无序序列中的第一个值进行交换即可。下方第一个框中就是从无序序列中查找最小的那个值的代码,第二个框就是交换的过程。如下所示:
3、运行结果
与上几个排序一样,我们输出的运行结果就是选择排序的详细的过程。下方就是选择排序的详细过程,如下所示:
六、测试用例
在博客要结尾的部分,我们仍然会给出本篇博客所使用的测试用例。下方就是本篇博客所使用的测试用例。上述的运行结果就是下方我们测试用例的输出结果。虽然输出的结果不同,但是我们用的都是一个测试函数,只是传入的排序对象不同。这也就是我们在程序的第一部分为什么要给出相应的协议定义的原因。测试用例如下所示:
本篇博客所涉及的所有代码都会在github上进行分享,下方是分享地址。后面会继续更新其他排序算法。
github代码分享地址: github.com/lizelu/Data…
作者:青玉伏案
出处:www.cnblogs.com/ludashi/
本文版权归作者和共博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
如果文中有什么错误,欢迎指出。以免更多的人被误导。