• 分治算法


    一. 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(nlg n)

    在这里插入图片描述

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

    在这里插入图片描述

    平均情况:O(nlgn)O(nlg 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);
    }
    
  • 相关阅读:
    TP5.0防跳墙访问
    TP5.0上传添加数据库
    抽象类与接口
    instanceof 关键字
    abstract 抽象类
    extends 继承
    单例模式
    类的加载过程和对象的创建
    静态成员变量和非静态成员变量的区别
    this关键字
  • 原文地址:https://www.cnblogs.com/lee3258/p/11997782.html
Copyright © 2020-2023  润新知