【知识强化】第七章 排序 7.5 归并排序和基数排序
本节课我们来学习归并排序。
什么是归并排序呢?那么根据名字,大家就能猜到,在该排序当中,是不是最重要一个操作就是归并操作呀。那么我们如何通过归并操作来实现将一个无序的序列排列成一个有序的序列呢?我们来看一下归并排序它大体的一个排序的过程,排序的流程。首先为大家列出了一个初始的一个无序的序列。当然这里为了方便,我们将每一个关键字用一个灰色的小矩形来代替。然后第一趟归并排序是如何进行的呢?我们将每一个关键字分为一组,那么这里有多少个灰色小矩形,有多少个关键字,是不是就有多少个组啊。
然后我们将相邻的两组归并为一组,也就是两个相邻的这样的灰色小矩形归并为一个蓝色的小矩形,这里蓝色小矩形有两个关键字,因为它是由两个灰色小矩形归并后的一个组合。那么在这里,蓝色小矩形它也是一个有序的一个归并的过程,我们是将它归并为一个有序的序列的。
那么我们这样,依次地将相邻的两组归并为一个有序的序列。
这样我们就实现了第一趟归并后的这样一个序列的一个状态。
大家在这里我们需要注意的一点是,
可能在归并时我们会有一个关键字落单,但是没关系我们直接将它摞起来就可以了。
这样我们就得到了第一趟归并后的这样一个序列的状态。我们将每秒的灰色小矩形组合成了一个有序的一个蓝色小矩形。好,接着我们实现第二趟归并。第二趟归并与第一趟是类似的。
我们也将相邻的两组关键字的值进行归并操作,归并为一个有序的一个新的一组,也就是两个蓝色的小矩形归并为一个有序的这样的橙黄色的小矩形。当然这里橙黄色小矩形当中表示的关键字的数目就是蓝色小矩形关键字数目的和。这样我们依次地将相邻的两组关键字序列进行有序的归并之后,是不是就得到了若干橙色的小矩形也就是剩下的这些组。接着这就是第二趟归并后的一个状态。
那么接着我们来进行第三趟归并,同样我们会将相邻的两组也就是相邻的两个橙色的小矩形当中的关键字进行一个有序的归并。那么这样我们就得到了三组,三个绿色的小矩形。这就是第三趟归并后的一个状态。
那接下来依旧我们还是进行类似的归并操作。相邻的两组进行归并,这样就是第四趟归并后的一个状态。
最后我们将剩下的两组进行归并,那么最终我们是不是就得到了一个有序的序列啊。因为我们在每次归并过程当中,都是将两组归并为一个有序的序列的。好,这就是归并排序它大致的一个执行流程。那么在这里同学们会发现,我们每一次是不是都是归并相邻的两组啊。
所以说在这里,我们现在实现的这种归并排序,也叫做2路归并排序。这个路就代表着我们每一次归并归并的组的数目。好,其实在考研当中最常见的就是这个2路归并排序。那么涉及到算法题目的时候,也会主要是2路归并排序。当然有的时候可能在选择题当中也会遇见3路归并排序。它的大体执行流程其实与2路归并是非常相似的。好,这就是归并排序的一个大体的执行流程。我们先简单地了解了一下归并排序它一个大体的一个执行过程,接下来我们就来看一下归并排序具体的实现的方式。那么我们用代码如何来实现呢?首先在归并排序当中,是不是最重要的一个操作就是归并相邻的两组关键字序列的这样一个操作呀。
所以说现在我们先编写一下有关合并两个有序的这样的线性表的操作的一个代码。那么大家对于这样的操作是不是非常熟悉啊,因为我们在之前讲述线性表相关知识点的时候,有带大家一起实现过合并两个有序线性表这样的操作代码。当然我们在数组当中,在单链表当中都是可以达到的,就是在大On时间复杂度就可以达到的。那么在这里,我们实现的是在顺序存储下的一个合并操作。
这就是在顺序存储下合并操作的代码。好,接下来我们就来回忆一下,如何实现合并两个有序线性表的这样的代码的编写。那么大家回忆一下,首先是不是要申请一个辅助空间啊。那么这样的辅助空间其实它是与两个有序线性表的长度之和是有关的。
但是在这里大家观察,我们申请了大小是不是n+1啊,n是不是表示着我们初始序列的元素的个数,也就是它的长度。这里为什么不是我们刚才合并的两个有序线性表呢?一个长度之和呢?为什么是与我们初始序列的长度、总长度是相关的呢?其实在这里我们申请了一个辅助数组就够了。那么当然这样的辅助数组它就应该为我们初始序列的一个长度。所以的在归并排序当中的合并操作,都利用了这样一个辅助的数组。所以我们要申请一个有关初始序列长度的这样一个总长度的一个辅助空间。当然这里还要加1,这里加1是为什么呢?是因为我们不用对应你下标的位置。那么我们序号是默认从1开始的,所以要申请n+1个空间,因为我们有一个下标为0空间是不做使用的。那么接下来我们就来看如何进行合并操作。这里叫Merge函数,那么它传入参数有4个,我们来看首先是我们这个初始序列的数组,然后是3个标记。大家可能会有疑问,那么这两个有序线性表呢?为什么我们申请了,我们传入了一个初始序列的这样一个数组就可以呢。我们是利用了3个标记来确定两个线性表的一个长度,一个部分的位置的。那么如何确定呢?我们来回忆一下,是不是每次合并都是相邻的两个组啊。那么这里我们只要知道那么第一个组的,这样第一个元素的它的位置以及它最后一个元素的位置。这样我们是不是就知道了下面一个组它第一个元素位置啊。然后我们又传入了一个最后一个组,第二个组它最后一个元素的位置,这样就通过了3个标记来标记了在该数组当中我们合并的是哪两组,合并的是哪两个有序的线性表。好,这就是传入的4个参数。接下来就是一个赋值操作。这里我们将对应想要合并的两个有序的线性表,一一地传入到了我们辅助数组的相应的位置。大家记住这里是相应的位置,因为我们合并哪一部分,就利用的辅助空间的对应的哪一部分的位置。那么这里,首先的一个赋值,是为了我们接下来的一个覆盖方便而赋值的。然后是不是就是一个for循环,第二个for循环就是我们对应的合并操作。大家回忆一下我们是如何合并的呢?我们是通过对应着两个这样的有序数组它其中的一个标记来合并的。这个标记初始化为数组当中的对应的第一个元素,那么当前这个第一个元素是不是就是最小或者是最大的那一个元素啊。我们来对比两个元素,两个线性表的对应标记下的这两个元素,哪一个更大或者是哪一个更小,它是不是就是应该传入到我们最终结果的那一个数组当中啊。所以说首先我们要在循环当中初始化两个标记i和j,