算法之路---归并排序

Posted by Haley_Wong on May 9, 2019

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。分治法就是将一个大问题分解成小问题然后递归求解,然后再将小问题的结果合并,最终得到问题的解。

归并操作

归并操作,也叫归并算法,指的是将两个有序的序列合并成一个有序的序列的方法。

算法描述

归并操作的工作原理如下:

第一步:申请空间,使其大小为两个有序序列之和,该空间用来存放合并后的序列。

第二步:设定两个指针,初始位置分别为两个有序列表的起始位置。

第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间(即之前申请的空间内),并移动指针到下一位置。

重复步骤3,知道某一指针超过序列尾。

最后,将另一序列剩下的元素直接复制到合并序列的尾部。

稳定性

归并排序是稳定的排序,即相等的元素的顺序不会改变。归并排序的比较次数小于快速排序的比较次数,但是移动次数一般多余快速排序的移动次数。一般用于总体无序,但是各子项相对有序的序列。

归并排序的OC实现

归并排序又分为自上而下和自下而上两种方式。

  • 自上而下,就是利用递归(或者迭代)将目标序列先拆分成两个小序列,然后对小序列再分别执行归并排序,以此类推直到小序列中只有一个元素,然后开始合并小序列。
  • 自下而上,就是直接从目标序列中,将两个相邻的元素两两归并,分别得到归并后的序列,再进行两两归并,直到最后只有一个序列,即为排序完成后的序列。

因为归并排序是比较好理解的一种排序算法,所以不做过多赘述,直接来看实现吧。

自上而下的归并排序:

/**
 归并排序(自上而下)
 
 @param randomNumbers 随机数组
 @return 排序后的数组
 */
+ (NSMutableArray *)mergeSort:(NSMutableArray *)randomNumbers
{
    if (randomNumbers.count <= 1) {
        return randomNumbers;
    }
    
    if (![randomNumbers isKindOfClass:[NSMutableArray class]]) {
        NSLog(@"参数类型错误,请使用NSMutableArray类型对象做入参");
        return nil;
    }
    
    // 创建一个临时数组,以防递归过程中频繁创建数组耗时
    NSMutableArray *tempArray = [NSMutableArray arrayWithCapacity:randomNumbers.count];
    
    [self sort:randomNumbers low:0 high:(int)randomNumbers.count-1 tempArray:tempArray];
    
    return randomNumbers;
}

+ (void)sort:(NSMutableArray *)randomNumbers low:(int)low high:(int)high tempArray:(NSMutableArray *)tempArray
{
    if (high <= low) {
        return ;
    }
    
    int mid = low + (high - low) / 2;
    
    [self sort:randomNumbers low:low high:mid tempArray:tempArray];
    [self sort:randomNumbers low:mid + 1 high:high tempArray:tempArray];
    [self merge:randomNumbers low:low mid:mid high:high tempArray:tempArray];
}

+ (void)merge:(NSMutableArray *)randomNumbers low:(int)low mid:(int)mid high:(int)high tempArray:(NSMutableArray *)aux
{
    int i = low;
    int j = mid + 1;
    // 先将数据存到临时数组中
    for (int z = low; z <= high; z++) {
        aux[z] = randomNumbers[z];
    }
    
    for (int k = low; k <= high; k++) {
        if (i > mid) {
            // 说明左子序列的元素都已用完
            randomNumbers[k] = aux[j++];
        } else if (j > high) {
            // 说明右子序列的元素都已用完
            randomNumbers[k] = aux[i++];
        } else if ([aux[i] intValue] < [aux[j] intValue]) {
            randomNumbers[k] = aux[i++];
        } else {
            randomNumbers[k] = aux[j++];
        }
    }
}

而自下而上的归并排序是这样的:

/**
 归并排序(自下而上)
 
 @param randomNumbers 随机数组
 @return 排序后的数组
 */
+ (NSMutableArray *)mergeSort2:(NSMutableArray *)randomNumbers
{
    if (randomNumbers.count <= 1) {
        return randomNumbers;
    }
    
    if (![randomNumbers isKindOfClass:[NSMutableArray class]]) {
        NSLog(@"参数类型错误,请使用NSMutableArray类型对象做入参");
        return nil;
    }
    
    int count = (int)randomNumbers.count;
    NSMutableArray *tempArray = [NSMutableArray arrayWithCapacity:randomNumbers.count];
    for (int sz = 1; sz < count; sz = sz + sz) {
        for (int lo = 0; lo < count - sz; lo += (sz + sz)) {
            // 让前两个序列归并,因为一个序列有sz个元素,所以中间元素是lo+sz-1,最大的元素是lo+sz+sz-1。
            // 为了防止越界,high 要去 lo + sz + sz -1 与 count -1中较小的元素
            int high = MIN(lo + sz + sz - 1, count - 1);
            [self merge:randomNumbers low:lo mid:lo + sz - 1 high:high tempArray:tempArray];
        }
    }
    
    return randomNumbers;
}

+ (void)merge:(NSMutableArray *)randomNumbers low:(int)low mid:(int)mid high:(int)high tempArray:(NSMutableArray *)aux
{
    int i = low;
    int j = mid + 1;
    // 先将数据存到临时数组中
    for (int z = low; z <= high; z++) {
        aux[z] = randomNumbers[z];
    }
    
    for (int k = low; k <= high; k++) {
        if (i > mid) {
            // 说明左子序列的元素都已用完
            randomNumbers[k] = aux[j++];
        } else if (j > high) {
            // 说明右子序列的元素都已用完
            randomNumbers[k] = aux[i++];
        } else if ([aux[i] intValue] < [aux[j] intValue]) {
            randomNumbers[k] = aux[i++];
        } else {
            randomNumbers[k] = aux[j++];
        }
    }
}

Have Fun!