算法设计与分析:分治思想 - 入门

本文介绍了分治思想的基本概念和判断条件,以归并排序为例阐述了分治在排序问题中的应用,并详细分析了如何利用分治策略解决求逆序对的个数问题,同时探讨了非均匀划分对算法复杂度的影响。文章强调理解分治的核心在于‘能否分,怎么分,怎么合’,并鼓励读者通过手动推导公式加深理解。

本文参考UCAS卜东波老师的计算机算法设计与分析课程完成

分治思想

分治思想可以归纳为两点:

  • 将一个复杂问题化简为多个简单的子问题
  • 将子问题的解组合成复杂问题的解
    如果满足以上两个条件,则称“大实例可归约为小实例”。

判断分治的条件

并非所有问题都能进行分治,通常我们通过观察问题的输入来获取划分的线索,如:

  • 一个字符串的部分仍为字符串
  • 一个集合的部分依然是集合
  • 一棵树去除根节点分成若干子树(树的问题很多用到分治策略,只对根节点进行操作)
  • 一个图的部分仍是一个图
    相对地,我们也可以根据观察问题的输出来判断子问题的解能否组和成复杂问题的解。

最经典的参考排序,排序得到的子序列可以进一步组成原序列的解

分治思想在排序问题的应用

给定一个乱序数组 A [ 0 , 1 , 2 , . . . , n − 1 ] A[0,1,2,...,n-1] A[0,1,2,...,n1],如何应用分治思想对其进行排序?

  • 1、最简单的情况

    • 当数组只有1个元素时,显然已经排好序,此时直接返回
    • 当数组只有2个元素时,只需比较两个元素大小,进行交换
  • 2、多个元素
    当数组中有n个元素的时候,我们总希望能将复杂问题转化为我们已知的,因此,可以将数组一分为2。
    A [ 0 , 1 , . . . , n − 2 ] , A [ n − 1 ] A[0,1,...,n-2],A[n-1] A[0,1,...,n2],A[n1] A [ n − 1 ] A[n-1] A[n1]是一个元素,假设前面n-1个元素已经有序,那么只需将 A [ n − 1 ] A[n-1] A[n1]插入到前面的有序数组中即可。

  • 3、递归调用
    我们可以重复上面的过程,将 A [ 0 , 1 , . . . , n − 2 ] A[0,1,...,n-2] A[0,1,...,n2]进一步分为 A [ 0 , 1 , . . , n − 3 ] , A [ n − 2 ] A[0,1,..,n-3],A[n-2] A[0,1,..,n3],A[n2],最终,我们会将数组划分成n个单一元素。

  • 4、合并子问题的解
    现在已经划分完毕,只需要依次将解合并即可。
    上述过程可以用下图表示:
    在这里插入图片描述

但上面的方式效率很低下,时间复杂度是 O ( n 2 ) O(n^2) O(n2),原因在于每一次划分都是划出一个元素,可以将这个过程用公式表达:
T ( n ) = T ( n − 1 ) + O ( n ) T(n) = T(n-1) + O(n) T(n)=T(n1)+O(n)
因为后面要将k个数进行合并(k<=n),所以总的使用时间可以计算如下:
T ( n ) ≤ T ( n − 1 ) + c n ≤ T ( n − 2 ) + c ( n − 1 ) + c n . . . ≤ T ( 1 ) + . . . + c n = O ( n 2 ) T(n) \le T(n-1) + cn \\ \le T(n-2)+c(n-1) + cn \\ ...\\ \le T(1) + ... + cn\\ = O(n^2) T(n)T(n1)+cnT(n2)+c(n1)+cn...T(1)+...+cn=O(n2)

实际上容易发现此时的归并排序已经退化成插入排序

  • 更改划分数量
    前面每次只划分一个元素,这次每次将元素对半划分,如下图:

    可以得到新的时间复杂度计算:
    T ( n ) = 2 T ( n 2 ) + n = 2 ( 2 ( T ( n 4 ) + n 2 ) + n = . . . = 2 k T ( n 2 k ) + k n , ( 当 n 2 k = 1 时 , k = l o g 2 n ) = n T ( 1 ) + n l o g 2 n , T ( 1 ) 为 常 数 时 间 复 杂 度 = c n + n l o g 2 n = O ( n l o g n ) T(n) = 2T(\frac{n}{2}) + n\\ = 2(2(T(\frac{n}{4})+\frac{n}{2}) + n \\ = ...\\ = 2^k T(\frac{n}{2^k})+kn,(当\frac{n}{2^k}=1时,k=log_2n)\\ = nT(1)+nlog_2n,T(1) 为常数时间复杂度\\ = cn + nlog_2n\\ =O(nlogn) T(n)=2T(2n)+n=2(2(T(4n)+2n)+n=...=2kT(2kn)+kn2kn=1k=log2n=nT(1)+nlog2n,T(1)=cn+nlog2n=O(nlogn)
    此时发现,时间复杂度简化到了nlogn,使得运算时间大幅减小。

  • 更一般的划分
    上述情况用了对半划分使得效率大幅提高,猜想若是将问题一次分成更多子问题,情况如何?假设将规模为n的问题,划分成a个子问题,每个子问题规模设为 n b \frac{n}{b} bn,可以得到公式如下:

    这里之所以是 n b \frac{n}{b} bn不是 n a \frac{n}{a} an,是因为并非所有问题中n都是被刚好分给所有子实例,各实例之间可能存在交叉数据,例如n分成3份,每份规模 n 2 \frac{n}{2}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值