分治算法

一. Divide-and-Conquer原理

简而言之, 分治算法就是一个问题的规模较大时不好解决, 但规模较小时又很好解, 那么我们就将大问题化成小问题, 依次求解小问题再合并成大问题的解, 当然, 不是所有问题都可以这么做.

设计过程分为三个阶段

1.Divide: 整个问题划分为多个子问题

注意:分解的这组子问题p1,p2,pmp_1 ,p_2 ,…p_m 未必一定是相同的子问题,即$p_i 和p_j $可以是分别完成不同任务的子问题

2.Conquer:求解各子问题(递归调用正设计的算法), 注意边界条件
3.Combine:合并子问题的解, 形成原始问题的解

在这里插入图片描述

Divide-and-Conquer 算法的分析

1.分析各阶段的复杂性

  • Divide 阶段的时间复杂性: D(n)
  • Conquer 阶段的时间复杂性: aT (n/b)
  • Combine 阶段的时间复杂性: C(n)

2.建立递归方程

  1. 设输入大小为 n, T(n) 为时间复杂性
  2. 当 n<c 时,T(n)= O(1)

在这里插入图片描述

3.求解递归方程得到问题的复杂度


例1: 最大最小值问题

输入:一个数组A

输出: 数组中的最大值和最小值

分析:

通常,直接扫描需2 n -2 次比较操作,下面给出一个复杂度为3n/2-2的算法

在这里插入图片描述

在这里插入图片描述

例2: 整数乘法

输入:n 位二进制整数 X 和 Y

输出:X 和 Y 的乘积

通常,计算X*Y 时间复杂性为 O(n2)O( n^2),我们给出一个复杂性为O(n1.59)O( n^{1.59}) 的算法。

在这里插入图片描述

例3: 快排

输入: 一个数组A

输出:一个数据排好序(升序)的数组A’

划分算法: 分界,并返回分解点的坐标

在这里插入图片描述

在这里插入图片描述

证明快排的正确性: 即证明循环不变量恒成立

循环不变量:数据或数据结构的关键性质, 依赖于具体的算法和算法特点

证明分三个阶段
(1)初始 阶段 :循环开始前循环不变量成立
(2)循环 阶段 :循环体每执行一次 循环不变量成立
(3)终止 阶段 :算法结束后,循环不变量保证算法正确

在这里插入图片描述

在这里插入图片描述

初始阶段 : j=p

算法迭代前: i =p 1, j=p, 条件 1 和 2 为真 . 算法第 1 行(即x=a[r]x = a[r] )使得条件 3为真

保持阶段:

设 j=k时循环不变量成立.往证j=k+1时不变量成立.

终止阶段

算法结束时, j=r, 产生三个集合:

  1. 所有小于等于x的元素构成的集合.
  2. 所有大于x的元素构成的集合.
  3. 由元素x构成的集合.

在这里插入图片描述

算法结束时
最后一个步骤将A[r]与A[i+1]互换.

在这里插入图片描述

算法性能分析

最好情况:O(nlgn)O(n\lg n)

在这里插入图片描述

最坏情况:O(n2)O(n^2)

在这里插入图片描述

平均情况:O(nlgn)O(n\lg n)

证明略

定理. 随机排序算法的期望时间复杂性为O(nlogn)

问题的下界

问题的下届即理论上算法最好的时间复杂度

如果一个算法的时间复杂度与问题的下界相同,那么则说该算法是最优的

下面说明了排序算法的下界:

在这里插入图片描述

减治算法

减治方法:仅通过 求解某一个子问题 的解得到原始问题的解

分治方法:递归 求解每一个子问题 ,然后通过合并各个子问题的解最后得到原始问题的解

例:中位数与次序统计问题

输入:一个数组A

输出: 第k大的元素

先求一个中位数q,将A中元素分到3个集合中去

在这里插入图片描述

例:找最近的点对

输入: 以为数组上的n个点

输出:距离最近的两个点

先排序再查找的时间复杂度O(n log n)

我们用分治算法

  1. 边界条件: 当只有两个点的时候,直接返回这个点对
  2. 求 Q 中点的中位数 m
  3. 划分: 用 Q 中点坐标中位数 m 把 Q 划分为两个大小相等的子集合Q1, Q2
    在这里插入图片描述
  4. 递归地在 Q 1 和 Q 2 中找出最接近点对(p1 ,p2 )和 (q1 ,q2)
  5. 合并:在 (p 1 , p 2) 、 (q 1 , q 2 )和某个 (p 3 , q 3 )之间选择最接近点对(x, y) 其中 p 3 是 Q 1 中最大点 q 3 是Q2 中最小点。
    在这里插入图片描述

在这里插入图片描述

凸包算法

输入: 二维平面上n个点的坐标

输出: (一个最少的)点的集合,该集合中所有点连成圈之后能包含所有点,如下图

在这里插入图片描述

注意: 这个圈要求以最少的点连成

分治算法:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

快速幂算法

输入: a, b,n

输出:(an)%b(a^n) \% b

分治算法:

数学支持:(ab)%c = (a%c)(b%c)%c

an%b=(an/2%c)(an/2%c)%ca^n \% b = (a^{n/2} \% c)(a^{n/2} \% c)\%c

T(n) = 2T(n/2) + O(1)

由master定理,可以得到算法复杂度为:log n

int PowerMod(int a, int n, int b)
{
    int ans = 1;
    a = a % b;
    while(n>0) {
        if(n % 2 = = 1)
        	ans = (ans * a) % b;  //n为奇数时,折半时会少一个数,最后一次折半会执行
        n = n/2;
        a = (a * a) % b;   // a此时已经为a%c了
    }
    return ans;
}

集合划分问题

问题:给出n个元素的集合,将其划分为两两不相交的m个子集,有多少种分法?

输入: n,m

输出: 分法数量k

问题分析:

我们需要将n个元素放进m个集合中, 那么显然的是:

  1. 当m > n时,划分是不可能的,返回0
  2. 当m = 1时不用划分,直接返回1
  3. 当m = n时不用划分,直接返回1

在这里插入图片描述
然后我们可以以此作为迭代基础,来进行迭代;又有如下规律:

setDiv(n,m)=setDiv(n1,m1)+msetDiv(n1,m)setDiv(n,m) = setDiv(n-1,m-1) + m*setDiv(n-1,m)

所以我们可以写出代码:

setDiv(n,m)
{
	if(n==m ||m=1 ) return 1;
    return setDiv(n-1,m-1) + m*setDiv(n-1,m);
}
posted @ 2019-06-29 10:52  lee3258  阅读(305)  评论(0编辑  收藏  举报