算法入门笔记

一、算法基本介绍

1. 算法的含义
  • 什么是算法?准确描述的问题求解的步骤

  • 什么是计算机算法?从特殊到一般的追求(实例->算法)

2. 算法的描述
  • 将问题求解过程,以操作步为单位,清晰准确的写下来

  • why描述:

    • 让目标读者看得懂
    • 便于分析算法的性质
    • 如果需要的话,有助于用程序实现
  • 描述方式包含:自然语言描述、流程图和伪代码

  • 三种控制结构:**顺序、条件、循环 **

3. 算法的分析
  • 正确性问题,能否达到目的?

    • 能否满足要求的结果解的类型:精确解、近似解、可行解:比如路线、最优解
    • 能否结束
  • 效果如何?

    • 时间效率和空间效率
      • 算法的时间效率(时间复杂性):操作步骤数与输入数据规模(值)之间的关系。
      • 大O计数法
      • 与最优解之间的差距(如果存在的话)
4. 算法的类型
  • 从复杂度分:多项式/指数算法

    主要在于效率和性能

    • 从应用场合分:数值计算/非数值计算
      • 数值计算方法:主要在于模拟和仿真,强调收敛与误差等概念
      • 非数值计算:不是计算过程不涉及到数值,二是主要目的在搜索、推理、决策、规划等需求的满足。
  • 从设计方法论分:枚举、贪心、分治、动态规划、启发式、回溯,这种划分有利于算法技巧的系统化学习。

  • 从数据的存储介质分:内存算法、外存算法

    • 主要区别在于随机访问数据的时间相等的假设成立与否。

二、欧几里得算法

1. 量水问题
  • 最大公约数,数学公式
    G C D ( x , y ) = a x + b y GCD(x,y) =ax+by GCD(x,y)=ax+by
2. 基本欧几里得算法

辗转相除法
if a > b,  G C D ( a , b ) = G C D ( a , a − b ) \text{if a > b, }GCD(a,b) = GCD(a,a-b) if a > b, GCD(a,b)=GCD(a,ab)
算法实现

Input:非负非全零整数 a,b
Output:
G C D ( a , b ) GCD(a,b) GCD(a,b)
While b<>0:
a , b = b , a % b a,b = b, a \% b a,b=b,a%b
print (“GCD(a,b)=”,a)

循环一定会变为0么?

特殊情况:a < b; a = 0; b = 0

3. 扩展欧几里得算法

Q:已知 G C D ( a , b ) = d GCD(a, b)= d GCD(a,b)=d,找出一组 x , y x,y x,y 满足 a x + b y = d ax+by=d ax+by=d

A:递归方法

数学公式
G C D ( a , b ) = d    ⟺    a x + b y = d , a x 1 + b y 2 = d , a % b = a − ( a / / b ) b , b x 2 + ( a % b ) y 2 = d , = > x 1 = y 2 ; y 1 = x 2 − ( a / / b ) y 2 GCD(a,b) = d \iff ax+by = d,\\ ax_1+by_2 = d, \\ a\%b = a-(a//b)b,\\ bx_2+(a\%b)y_2 = d,\\ =>x1= y2;y_1=x_2-(a//b)y_2 GCD(a,b)=dax+by=d,ax1+by2=d,a%b=a(a//b)b,bx2+(a%b)y2=d,=>x1=y2;y1=x2(a//b)y2
辗转相除、终止条件、逐层计算

  • 算法的正确性
  • 算法效率分析:k
  1. 欧几里得算法的应用
  • RSA加密算法:质数 ab,求 ax % b = 1

三、二分法

1. 二分搜索

二分搜索原理

  1. “>, <, =” 满足传递特性
  2. 二分搜索仅适用于有序排列的搜索对象

伪代码

​ 输入:a[]升序数组,key,查找对象

​ 输出:找到,返回序号;否则查找失败。

  1. low = 0; high = Len(a) - 1
  2. while(low <= high):
  3. ​ mid = (low+high) //2; – 向下取整
  4. ​ if(key == a[mid]) return mid
  5. ​ if(key < a[mid]) high = mid - 1
  6. ​ if(key > a[mid]) low = mid + 1
  7. return not found

算法的正确性讨论

  1. 是否一定会计算结束
  2. 是否一定查找到?

算法的效率

  • 逐次比较: O ( n ) O(n) O(n)

  • 二分搜索: O ( log ⁡ n ) O(\log n) O(logn),可以使用树型结构将这个结果表示出来

2. 二分法求奇次方程组的一个实根

求解二次方程的解

因式分解 或者 同解公式

?高阶方程

二分法的思想:
f ( a ) f ( b ) < 0 , 则存在 x ∗ ∈ ( a , b ) ,满足 f ( x ∗ ) = 0 ⟶ 分而治之的二分法求解思想 f(a)f(b)<0,则存在x^*\in (a,b),满足f(x^*)=0\longrightarrow 分而治之的二分法求解思想 f(a)f(b)<0,则存在x(a,b),满足f(x)=0分而治之的二分法求解思想
什么时候停止计算

  1. 基于函数值 f ( x ) f(x) f(x)
  2. 基于区间长度,满足一定的阈值则停止计算

哪些方程可以使用二分法计算?

​ 函数的连续性、单调性【不单调可能会漏掉某些实根】

伪代码

算法的正确性

如何确定初值?使用分割法

算法的效率

  1. 使用区间长度作为精确要求,则收敛速度 l o g ( d / ϵ ) log(d/\epsilon) log(d/ϵ)
  2. 函数值确认:无法确认,陡峭则缓慢、平缓则快速

其他近似求解方法

牛顿法等等,用切线计算

四、最优编码树

1. 信息编码

编码:字符映射到二进制序列

解码:二进制序列还原成对应的字符

压缩(有损/无损):用较少的01序列描述原始信息

几种常用的常用的编码方式:

  • ASCII编码:将数字、字母和一些常用符号用一个字节编码
  • GBK/GB2312:针对简体字的编码,每个汉字用两个字节编码
  • Unicode:
  • UTF-8:

定长和可变长编码:

  • 定长编码:容易得到对应的解码
  • 不定长编码:
    • 前缀码问题:某些字符编码是其他字符编码的前缀,则容易引发混淆
    • 编码长度:

如何提升编码效率?

  • 编码效率:编码总长度/字符数= ∑ f i × L i \sum{f_i}\times L_i fi×Li f i f_i fi 为频率, L i L_i Li为编码长度
  • 出现频率高的字符用较短的编码、出现频率较低的用较长的编码,如何设计最优编码呢?
2. 哈夫曼编码

一种信息的无损压缩方法

编码数

  • 二叉树:每个节点至多只有两个子节点【0 1 编码】

    长度不同具有唯一性,但是会导致前缀码问题,无法正确解码

  • 叶节点:

编码效率与码源频次的关系:码位均值最低,编码效率最高

  • 构建码位均值最低的编码树:哈夫曼编码树,哈夫曼编码也被称为最优编码
3. 哈夫曼编码算法

构建哈夫曼编码——自下向上/自上向下?

  • 自下而上的方法避免了已经形成的叶节点变成子节点、需要再调整的过程。
  • Huffman 算法:自下而上构建的最优二叉编码树。

构建最优编码树——1个子节点/2个子节点?

  • 优化编码树:满二叉树(否则可以将唯一子节点提升代替其父节点、码位减少)

Huffman编码算法描述:

  • 构建Huffman树节点:

    从频率集合中选取最小的两个节点 i j、创建其父节点 k, f k = f i + f j f_k=f_i+f_j fk=fi+fj

  • 调整频率集合:

    添加新建父节点的频率 f_k,删除两个子节点频率 f_i、f_j

两个数据结构

  • 一位数组 f[],存放频率
  • 二维数组 node[][],huffman树节点
    • 符号、频率、左右子节点

Huffman编码伪代码:
在这里插入图片描述

遍历哈夫曼树形成编码(深度优先搜索):
在这里插入图片描述

算法的正确性:

  • 哈夫曼编码是最优编码、但不是唯一最优编码
  • 算法运行的正确性:
    • 创建哈夫曼树
    • 遍历哈夫曼树

算法效率:

  • 创建哈夫曼树 O ( n 2 ) O(n^2) O(n2)
  • 遍历哈夫曼树 O ( n ) O(n) O(n)

总的时间效率 O ( n 2 ) O(n^2) O(n2),优化的数据结构 O ( n log ⁡ n ) O(n\log n) O(nlogn)

贪心策略:

每一步都选择当前最好的选择,从而达到全局最优的结果

贪心算法是否能拿到最优结果?不一定,需要看局部最优和全局最优之间的关系

五、优化互联互通的成本(最小生成树)

1. 连通图与生成树

节点、边、图|生成树、支撑树【无环的连通图】

无环连通图:树,如果有n个节点、那么将他们联通起来需要的最小边数为n-1

图的数据表示:边列表

  • 可以表示任意规模的图
  • 节点集合隐含在边的信息中

算法思路、算法描述与算法分析
在这里插入图片描述

算法的效率(假设输入图有m条边): O ( m × n ) O(m \times n) O(m×n)

2. 最小生成树算法

加权图:每条边上关联有一个数值,用来表示在两个节点直接连通的的代价,如修路的成本、铺设管线的难度。

最小生成树:

基于一种生成树算法改造而得的最小生成树的算法
在这里插入图片描述

算法的效率(假设输入图有m条边): O ( m × n ) O(m \times n) O(m×n)

3. 算法的正确性

算法分析:为什么它给出的是最小生成树?

树:添一条边,生成一个环、去掉一条边,则会又长出一棵树。

算法的正确性:

  • 是否包含权值最小的边?反证法,成环
  • 是否存在在E中的一条两端点恰好有一个在V中且权值不是最小的边,生成的树是最小生成树?反证法,假设前i步都是最优结果,第i+1步骤出现问题,反证算法失败。

算法讨论:为什么说它是“贪心算法”?

  • 局部最优vs全局最优

六、斐波那契的三种求解算法

斐波那契数列:

来源:

​ 上月成兔+上月幼兔 = 上月总对数

​ 上月幼兔 = 再上月成兔

​ 上月成兔 = 再上月成兔+再上月幼兔=再上月总

​ 当月兔子 = 当月成兔+当月幼兔=上月总+上月成兔= 上月总+再上月总

then F ( n ) = F ( n − 1 ) + F ( n − 2 ) F(n) = F(n-1)+F(n-2) F(n)=F(n1)+F(n2)

1. 递归方法

自上而下,算法效率为 O ( 2 n ) O(2^n) O(2n).适用于其他方法难以解决的问题

缺点在于状态的保存与恢复以及重复计算时间,需要额外的时间消耗

可以提高算法效率嘛?

2. 动态规划(记忆法)

是一种自下而上的计算方法,记录中间过程的解,避免大量的重复计算。

算法复杂度为 O ( n ) O(n) O(n),是一种空间换时间的计算方法。

适用于存在大量重复计算的问题。

3. 矩阵解法

快速幂运算

  • 求解 a n a^n an,将a自乘n-1次,时间效率为 O ( n ) O(n) O(n)
  • 任意整数n 可以表述为 2的k次幂序列之和,效率为 O ( log ⁡ 2 ( n ) ) O(\log_2(n)) O(log2(n))

快速幂伪代码:
在这里插入图片描述

矩阵解法效率分析:

  • 矩阵算法效率 O ( log ⁡ 2 ( n ) ) O(\log_2(n)) O(log2(n))
  • 记忆法 O ( n ) O(n) O(n)

七、最大收益的投资组合

1. 背景

问题的一般描述:将n元钱分成m份,每份>= 0
在这里插入图片描述

2. 动态规划

从特殊到一般:在i个项目上的最佳投资的回报能通过在i-1个项目上的回报进行表达
在这里插入图片描述

算法思路:
在这里插入图片描述

这种计算思路是有最后的兜底的,即
F 1 ( x ) = f 1 ( x ) F_1(x)=f_1(x) F1(x)=f1(x)

动态规划工作表:

在这里插入图片描述

3. 最佳投资组合

在知道最优回报时,如何获取对应的最优回报组合呢?

既得到最优回报,也得到最优回报组合

动态规划既记录 F i ( x ) F_i(x) Fi(x),同时也记录此时在第 i i i 个项目上的投入。

算法描述

在这里插入图片描述

伪代码(memo对应工作表,payoff对应各项回报)

在这里插入图片描述

小结
在这里插入图片描述

如果项目的回报函数不是单调非减的,则算法还有效么?在算法计算时需要加深对数据的理解以及表达式的抽象。

八、路径规划(节点之间的最短路径)

1. 问题引入以及图论基础知识

给出一个图结构,如何得出两个节点之间的最短距离呢?

图的基本概念
在这里插入图片描述
在这里插入图片描述

单源:求某个源点到其他点间的最短路径——Dijkstra

多源:给定连通图,求任意节点对间的最短路径——Floyd算法

2. 单源最短路径 Dijkstra 算法

依次找到最短路径,下一个最短路径取决于已得到的最短路径+边值。

依次找到最短路径的方法在这里插入图片描述
在这里插入图片描述

前驱节点:逻辑类似于最优组合的第i项目

算法伪代码

在这里插入图片描述

算法的效率分析:更适用于边数较少的图计算。在这里插入图片描述

3. 多源最短路径 Floyd 算法

适合于连接边比较多的稠密网络

构建子问题:i ->j节点的最短路径

  • 节点 1~n,矩阵D[i,j] 表示i-j的距离
  • $D^0[i,j] $:初始路径矩阵
  • $D^1[i,j] $:经过中转节点“1”转发
  • $D^2[i,j] $:经过中转节点“2”转发

算法公式
D k [ i , j ] = m i n { D k − 1 [ i , j ] , D k − 1 [ i , k ] + D k − 1 [ k , j ] } D^k[i,j] = min\{D^{k-1}[i,j], D^{k-1}[i,k]+ D^{k-1}[k,j]\} Dk[i,j]=min{Dk1[i,j],Dk1[i,k]+Dk1[k,j]}
动态规划的基本思想:

  • 问题分解成相关联的子问题
  • 当前子问题需要借助前面子问题的解(不发生重复计算)

如何得到最短路径经过的节点?在发生路径值更新时,更新前驱节点:

在这里插入图片描述

伪代码实现
在这里插入图片描述

算法的正确性分析:

严格的证明要使用数学归纳法:

在这里插入图片描述

单源路径算法和多源最短路径的算法比较
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值