rrobber

导航

算法分析与设计学习总结_1

算法分析与设计

一、算法概述

1.1算法和过程

(1)算法和过程都是解决问题的一种方法的逐步描述

(2) 他们都是由若干条指令组成的有穷序列;每条指令意义确定;具有零个或多个输入;产生若干个输出。

(3) 算法的执行时间是有限的(终止性);过程的执行时间可能是无限的。

1.2算法

(1)程序和算法

​ ① 程序是某个算法 / 过程 在计算机上的具体实现;程序依赖于程序设计语言,甚至依赖于计算机结构。

​ ② 算法是脱离具体的计算机结构和程序设计语言的。

(3)算法的复杂性:算法运行时所需要的计算机资源的量多少。(所需资源量越多则复杂性越高,反之所需资源量越少则复杂性越低)

​ ① 时间复杂性:需要时间的资源量;空间复杂性: 需要空间的资源量。

​ ② 决定算法复杂性的因素:求解问题的规模;具体的输入数据;算法本身的设计。

二、递归和分治

2.1递归

(1)递归概念

​ ① 递归算法 P = if B then Q else β(S, P) 其中,B为递归终止条件,S和Q都不包含P。

​ ② 递归算法的思想:将对较大规模对象的操作归结于对较小规模对象实施同样的操作。

​ ③ 递归元: 递归算法的变元。递归元的变化在递归定义中定义,它的变化能导致递归算法的终止。

(2)汉诺塔问题

void Hanoi(int n, int Fr, int To, int As)
       {
             if  (n > 0) {
                Hanoi(n–1, Fro, Ass, To);
                Move(Fro, To);
                Hanoi(n–1, Ass, To, Fro)}
        }

​ ① 汉诺塔问题的时间复杂性为 O(2n)

(3)常见递归形式

  • 多变元递归:递归元多于一个的递归

    /*
    整数划分问题:将一个正整数n表示为一系列正整数之和,
    n = n1 + n2 +…+nk,其中n1≥n2≥…≥nk≥1, k≥1
    (例如ρ(6) = 11,即整数6的划分数为11:
        6, 5+1, 4+2, 4+1+1,  3+3, 3+2+1, 3+1+1+1
        2+2+2, 2+2+1+1, 2+1+1+1+1, 
        1+1+1+1+1+1 )
    */
    q(n, m) { 
           if (n < 1) || (m < 1)      return 0;
           if (n == 1) || (m == 1)  return 1;
           if (n == 1) || (n < m)    return 1 + q(n, n–1);
           return q(n, m–1) + q(n–m, m);  }
    //整数n的划分数ρ(n) = q(n, n)	
    
  • 多步递归:递归函数f(x, y),其中y是递归元,不仅与f(x, y–1)有关,而且与f(x, y–2),……,乃至f(x, 0)有关。(Fibonacci函数)

    QQ图片20231225135217

  • 嵌套递归:递归调用中又含有递归调用,又称为多重递归

    QQ图片20231225135418

  • 联立递归:同时定义几个函数,它们彼此相互调用,从而形成递归,又称间接递归。

    QQ图片20231225135516

    (4)递归算法的时间复杂性

    ​ ① 递归元的递减方式:减法(n - b)和除法(n / b)

    ​ ② 递归时间复杂性的递归方程:

    QQ图片20231225161516

    ​ ③ 当递减方式为 n-- 时,时间复杂性T(n) = O(an)

2.2分治法

(1)分治法

​ ① 分治法基本思想:n将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相同。递归地解这些问题,然后将各个子问题的解合并在一起,从而得到原问题的解。

​ ② 分治法的一般算法模式

Divide-and-Conquer(P)
{//|P|<=n0表示P的规模不超过阈值n0,可直接求解
    if (|P|<=n0) return Adhoc(P);
   divide P into smaller subinstants P1, .., Pk;
   for (i =1; i <= k; i++) 
         yi = Divide-and-Conquer(Pi);
   return Merge(y1, …, yk);
} //算法Merge(y1, …, yk)表示将子问题的解合成P的解

​ ③ 二分搜索的算法复杂度:logn+1;时间复杂度:O( logn )。

​ ④ 分治法复杂度低于原问题,但是整体上能否降低复杂性还取决于合并。通过降低合并复杂性可以降低原问题求解的复杂性。

(2)大整数的乘法

(3)棋盘覆盖问题

三、贪心算法

3.1贪心算法

(1)贪心算法

​ ① 基本思想:贪心算法每次选择目前最优的解,即通过一系列局部最优来获得整体最优。

​ ② 贪心算法的基本要素:贪心选择性质。(指所求问题的整体最优解可以通过一系列局部最优解的选择,即贪心选择来达到。)

​ ③ 贪心算法通常以自顶向下的方式进行,每次贪心选择就将原问题转化为规模更小的子问题。

(2)单源最短路径问题

​ ① 贪心选择策略:选择从源v出发目前用最短的路径所到达的顶点,这就是目前的局部最优解。

​ ② 贪心基本思想:首先设置一个集合S;用数组dis[]来记录v到S中各点的目前最短路径长度。然后不断地用贪心选择来扩充这个集合,并同时记录或修订数组dis[];直至S包含所有V中顶点。

(2.1)Dijkstra算法

​ ① 算法策略:由近到远逐步计算,每次找最近顶点距离作为最短路径长度,然后再从该最近者出发,即依据最近者修订到各顶点距离,再选出新的最近者,直到走完所有顶点。

​ ② 算法举例

QQ图片20231225171731

​ ③ Dijkstra算法的贪心选择性质:若u是V–S中具有最短路径的特殊顶点,就将u选入S,并确定了从源到u的最短路径长度D[u]。

​ ④ Dijkstra算法的时间复杂度:O(n2)。因为他有两层循环,外层循环n次,内层的两个循环一个是选出最小距离,一个是修订距离数组,他们次数都是n/2,所以时间复杂度为O(n2)。

3.2最小生成树问题

(1)Prim 算法

① 基本思想:在保证连通的前提下依次选出权重较小的n – 1条边(在实现中体现为n个顶点的选择)。

(下图中T为G的最小生成树)

QQ图片20231225163750

② 算法中的数据结构:

  • 用连接矩阵C[ i ] [ j ] 表示图,Cn [ i ] [ j ] 为结点 i 到 j 权重。
  • 对图中每个顶点设两个数组 closest [ j ] 和 lowcost [ j ] :closest [ j ] 是 s 中与 j 最近的顶点,即为选中的边; lowcost [ j ] 是相应边的权重。

③ 算法实现

Prim(int n, Type **c) {
    int j = 1; s[j] = true;
    for(int i = 2; i <= n; i++) {
        closest[i] = 1;
        lowcost[i] = c[1][i];
        s[i] = false;
    }
    for(int i = 1; i < n; i++) {
        min = inf;
        for(int k = 2; k <= n; k++) {
            if(lowcost[k] < min && !s[k]) {
                min = lowcost[k];
                j = k;
            }
         s[j] = true;
         for(int k = 2; k <= n; k++) {
             if(c[j][k] < lowcost[k] && !s[k]){
                 lowcost[k] = c[j][k];
                 closest[k] = j;
        }
    }
}

④ 算法示例

QQ图片20231225165524

(2)Kruskal算法

①基本思想:在保证无回路的前提下依次选择权重较小的n – 1条边。

QQ图片20231225165658

② 算法中的数据结构:

  • 数组 e [ ] [ ] 表示图的边。e [ i ] [ u ] ,e [ i ] [ v ] 表示边 i 的两个端点,e [ i ] [ w ] 表示边 i 的权重。
  • 使用 sort() 函数将数组按权重 w 从小到大排列。
  • 使用 initialize(n) 初始化;find(u) 找顶点所在集合;union(a,b) 合并集合a,b ;重载 != 判断集合是否相等。

③ 算法举例

QQ图片20231225170506

(3)对比 Prim算法 和 Kruskal算法

​ ① Prim算法为两重循环,外层循环n次,内层循环O(n),所以复杂性为O(n2) 。

​ ② Kruskal算法中,设边数为e,则边排序的时间为O(e),确定边的时间为O(loge),所以整个时间复杂性为O(eloge)。

​ ③ 点比边多时,用Kruskal算法更好,反之用Prim。

(4)用Kruskal算法得到的生成树T*必是最优树。

posted on 2024-01-20 17:20  rrobber  阅读(17)  评论(0编辑  收藏  举报