归并排序

归并排序

归并排序(Merging Sort)也是一种使用分治法来实现的排序算法,它是现代计算机创始人John von Neumann于1945年发明的。

归并排序思路

先递归的把数组划分为两个子数组,一直递归到数组中只有一个元素,然后再调用函数把两个子数组排好序,因为该函数在递归划分数组时会被压入栈,所以归并排序真正的作用是对两个有序的子数组进行排序;

归并排序运行过程

  1. 判断参数的有效性,也就是递归的出口;
  2. 首先什么都不管,直接把数组平分成两个子数组;
  3. 递归调用划分数组函数,最后划分到数组中只有一个元素,这也意味着数组是有序的了;
  4. 调用排序函数,把两个有序的数组合并成一个有序的数组;
  5. 进行排序,让两个数组的元素进行比较,把大的/小的元素存放到临时数组中,如果有一个数组的元素被取光了,那就直接把另一数组的元素放到临时数组中,然后把临时数组中的元素都复制到实际的数组中;
初始值: 6  5  3  1  8  7  2  4

首先将数组分为长度为 2 的子数组,并使每个子数组有序:
[6, 5]  [3, 1]  [8, 7]  [2, 4]
[5, 6]  [1, 3]  [7, 8]  [2, 4]

然后再两两合并,并使每个子数组有序:
[6, 5, 3, 1]  [8, 7, 2, 4]  
[1, 3, 5, 6]  [2, 4, 7, 8]

最后将两个子数组合并:
[6, 5, 3, 1, 8, 7, 2, 4]
[1, 2, 3, 4, 5, 6, 7, 8]

归并排序代码

- (void)viewDidLoad {
    [super viewDidLoad];

    NSMutableArray * array = [NSMutableArray arrayWithObjects:@8,@7,@6,@5,@4,@3,@2,@1, nil];
    //调用排序
    [self mergeSortArray:array];
}

- (void)mergeSortArray:(NSMutableArray *)array {
    //创建一个副本数组
    NSMutableArray * auxiliaryArray = [[NSMutableArray alloc]initWithCapacity:array.count];

    //对数组进行第一次二分,初始范围为0到array.count-1
    [self mergeSort:array auxiliary:auxiliaryArray low:0 high:array.count-1];
}

- (void)mergeSort:(NSMutableArray *)array auxiliary:(NSMutableArray *)auxiliaryArray low:(int)low high:(int)high {
    //递归跳出判断
    if (low>=high) {
        return;
    }
    //对分组进行二分
    int middle = (high - low)/2 + low;

    //对左侧的分组进行递归二分 low为第一个元素索引,middle为最后一个元素索引
    [self mergeSort:array auxiliary:auxiliaryArray low:low high:middle];

    //对右侧的分组进行递归二分 middle+1为第一个元素的索引,high为最后一个元素的索引
    [self mergeSort:array auxiliary:auxiliaryArray low:middle + 1 high:high];

    //对每个有序数组进行回归合并
    [self merge:array auxiliary:auxiliaryArray low:low middel:middle high:high];
}

- (void)merge:(NSMutableArray *)array auxiliary:(NSMutableArray *)auxiliaryArray low:(int)low middel:(int)middle high:(int)high {
    //将数组元素复制到副本
    for (int i=low; i<=high; i++) {
        auxiliaryArray[i] = array[i];
    }
    //左侧数组标记
    int leftIndex = low;
    //右侧数组标记
    int rightIndex = middle + 1;

    //比较完成后比较小的元素要放的位置标记
    int currentIndex = low;

    while (leftIndex <= middle && rightIndex <= high) {
        //此处是使用NSNumber进行的比较,你也可以转成NSInteger再比较
        if ([auxiliaryArray[leftIndex] compare:auxiliaryArray[rightIndex]] != NSOrderedDescending) {
            //左侧标记的元素小于等于右侧标记的元素
            array[currentIndex] = auxiliaryArray[leftIndex];
            currentIndex++;
            leftIndex++;
        } else {
            //右侧标记的元素小于左侧标记的元素
            array[currentIndex] = auxiliaryArray[rightIndex];
            currentIndex++;
            rightIndex++;
        }
    }
    //如果完成后左侧数组有剩余
    if (leftIndex <= middle) {
        for (int i = 0; i<=middle - leftIndex; i++) {
            array[currentIndex +i] = auxiliaryArray[leftIndex +i ];
        }
    }
}

分析下上面代码:其实上面的代码主要的是两个函数,第一个是划分数组函数,第二个是对两个有序数组合并的归并函数;上述的实现代码中重点是递归划分数组,好久不用递归,这里是想了又想想了又想,终于想明白了。

性能(算法时间、空间复杂度、稳定性)分析

  1. 时间复杂度

    时间复杂度为 O( nlogn )。

    公式就不推导了,推导过程参考地址:http://blog.youkuaiyun.com/yuzhihui_no1/article/details/44198701#t2

  2. 空间复杂度

    空间复杂度为O(n)。

  3. 稳定性

    归并排序是稳定的。

总结

在时间上也是非常有效的(最差时间复杂度和最优时间复杂度都为 O(nlogn) ),但是这种算法很消耗空间,一般来说在内部排序不会用这种方法,而是用快速排序;外部排序才会考虑到使用这种方法;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值