算法基础--递归入门の归并排序

本文只是自己的笔记,并不具备过多的指导意义。

为了理解很多都使用了递归,而不是自己通过while进行压栈处理。
代码的初衷是便于理解,网上大神优化过的代码很多,也不建议在项目中copy本文代码。

目录

  • 归并排序
    • 如何合并两个有序数组
    • 使两端分别有序
    • 最外侧还有一个入口方法
    • 图解排序过程
  • 递归时间复杂度的估算
    • master公式

归并排序

众所周知,分治策略中使用递归来求解问题分为三步走,分别为分解、解决和合并。

所以归并排序中心思想是通过二分的方式,将一个段数组拆分成左右两端,然后进行合并

  • 如何合并两个有序数组

通过将最小值依次外排的方式,进行合并

  1. 两个指针p1p2指向两个数组的首位置,每次将较小的值外排进辅助数组并且右移指针
  2. 当一个指针越界后,将另一个剩余的元素放入辅助数组
  3. 最后的辅助数组将是一个有序数组。
/// 对一个数组的两个有序分段进行整合排序
///
/// - Parameters:
///   - arr: 数组
///   - left: 左侧起点
///   - mid: 中心点,左侧末尾
///   - right: 右侧末尾
func merge(arr: inout [Int] ,left: Int ,mid: Int ,right:Int) {
    
    var help : [Int] = [Int](repeating: 0, count: right-left+1) //辅助数组
    var p1 = left//左侧指针
    var p2 = mid+1 //右侧指针
    var i = 0 //容器指针位置
    
    while p1<=mid && p2<=right {
        if arr[p1] <= arr[p2] {//左侧指针位置小于等于右侧指针位置
            help[i] = arr[p1] //将左侧指针位置放入辅助数组
            p1 = p1+1 //左侧指针右移
        }else {//右侧指针大于左侧指针位置
            help[i] = arr[p2] //将右侧指针位置放入辅助数组
            p2 = p2+1 //左侧指针右移
        }
        i = i+1  //容器指针右移
    }
    
    //到这里,p1,p2之中已经有一个指针越界了
    //没有越界的那个,重复上面的操作写入辅助数组
    while p1<=mid {
        help[i] = arr[p1]
        p1 = p1+1
        i = i+1
    }
    while p2<=right {
        help[i] = arr[p2]
        p2 = p2+1
        i = i+1
    }
    //写入完毕,用辅助数组替换进原数组
    i = 0
    for index in left...right { //left,left+1...right-1,right
        arr[index] = help[i]
        i = i+1
    }
}

之所以先说这个,是因为这个方法是核心方法,并且写完滞后直接将可以测试。
测试通过之后,再去测试下面的递归方法可以更加准确的确定问题所在。

  • 使两端分别有序

通过递归的方式,将长数组最终分解成有序的数组进行排序处理。

比如[4,2,6,1,9,7]

/// 在一个数组的某一段上进行排序
///
/// - Parameters:
///   - arr: 数组
///   - left: 左侧
///   - right: 右侧
func mergeProcess(arr: inout [Int] ,left: Int ,right:Int) {
    
    if left==right { //左右相等,说明这次要调整的只有一个数
        return
    }
    
    let mid = (left+right)/2 //取得中心点位置
    mergeProcess(arr: &arr, left: left, right: mid)//对左半边进行排序
    mergeProcess(arr: &arr, left: mid+1, right: right)//对右半边进行排序
    //此时左右两侧都已经排序完成
    merge(arr: &arr, left: left, mid: mid, right: right)//对左右两侧进行合并排序
}
  • 最外侧还有一个入口方法
/// 归并排序
///
/// - Parameter arr: 数组
func mergeSort(arr: inout [Int]) {
    if arr.count<2 {
        return
    }
    mergeProcess(arr: &arr, left: 0, right: arr.count-1)
}

时间复杂度O(N * logN),额外空间复杂度O(N)

  • 图解排序过程

网上还看到一个动图,结合上面的应该更容易理解了。

这个过程中,不管数组多长,最后都将被划分成2个单位长度的数组进行排序。
然后向上依次合并并且排序。

这也很好的体现出了分治或者递归的思想所在,将大样本划分成小样本并依次解决。


递归时间复杂度的估算

只说一种普遍的通用情况

  • master公式
T(N) = A*T(N/B) + O(N^D)

量本量为N的情况下,整个样本被划分成B样本量,共执行A次
出去调用递归过程之外,剩下的部分的时间复杂度O(N^D)

1) log(b,a) > d -> 复杂度为O(N^log(b,a)) 
2) log(b,a) = d -> 复杂度为O(N^d * logN) 
3) log(b,a) < d -> 复杂度为O(N^d)

以归并排序为例:

func mergeProcess(arr: inout [Int] ,left: Int ,right:Int) {
    
    if left==right { //左右相等,说明这次要调整的只有一个数
        return
    }
    
    let mid = (left+right)/2 //取得中心点位置
    mergeProcess(arr: &arr, left: left, right: mid)//T(N/2)
    mergeProcess(arr: &arr, left: mid+1, right: right)//T(N/2)
  
    merge(arr: &arr, left: left, mid: mid, right: right)//O(N)
}

T(N) = T(N/2) + T(N/2) + O(N) = 2T(N/2) + O(N)
A = 2,B=2,D=1

带入master公式log(b,a) = 1,与d值相等。

于是归并排序的时间复杂度为O(N * logN)

参考资料

【图解数据结构】 一组动画演示归并排序
左神牛课网算法课
十大经典排序算法(动图演示)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    蚁前阅读 5,101评论 0 52
  • 概述:排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    每天刷两次牙阅读 3,706评论 0 15
  • 一些概念 数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这...
    Winterfell_Z阅读 5,531评论 0 13
  • 如果要问这个世界有谁不了解我,那大概就是我自己了。不管是公开的自我,秘密的自我,盲目的自我还是未知的自我,我好像都...
    空洞美阅读 284评论 0 2
  • 冬日的暖阳斜斜的撒进列车的窗户,列车在行,暖阳在走,心也在跟着流动,,, 9漂泊,是每个男儿的梦,可以越高山,穿沙...
    唐古拉的雪阅读 189评论 1 0