算法笔记

算法笔记

C1-2

算法的五个重要特性:

  1. 输入
  2. 输出
  3. 有穷性
  4. 确定性
  5. 可行性

算法时间复杂度的含义:算法执行过程中所需要的基本运算次数

插入排序O(n^(1-2)):从第二个元素开始,每次将当前元素和这个元素之前的元素一一比较,小则交换,大则停止。最后得到一个升序

C3

三种记号的定义:

g(n)与机器和语言无关

  1. O(g(n)) = { f(n): 存在正常量 c 和 n0 , 使得对所有 n ≥ n0 有0 ≤ f(n) ≤ cg(n) }
  2. Θ(g(n)) = { f(n): 存在正常量 c1 , c2 , 和 n0 , 使得对所有 n ≥ n0 , 有0 ≤ c1g(n) ≤ f(n) ≤ c2g(n) }
  3. Ω(g(n)) = { f(n): 存在正常量 c 和 n0 , 使得对所有 n ≥ n0, 有0 ≤ cg(n) ≤ f(n) }

当渐进记号出现在等式左边,则表示无论等号左边的匿名函数如何选择,总有办法选取等号右边的匿名 函 数 使 等 式 成 立

限界函数的性质:

  • 传递性:f(n)=O(g(n)) and g(n)=O(h(n)) ------->f(n)=O(h(n))
  • 自反性:f(n)=O(f(n))
  • 对称性:只有Θ
  • 转置对称性(对称性是Θ,转置是O和Ω)

相关定理:

  1. 若 是一个 n 的 m 次项式,其中 ai 为常量,i = 0,…,m, 且 am > 0,则有 A(n) = Θ(n ^m)
  2. 估算复杂性(函数阶的大小)的定理:
    • (lgn)^x < n
    • n^x < 2^n
    • O(2^n ) < O(n!) < O(n^n )
  3. 如果 d(n) 是 O(f(n)),e(n)是 O(g(n)),那么 d(n)+e(n) 是 O(f(n)+g(n)) —— 加法法则
  4. 如 果 d(n) 是 O(f(n)) , e(n) 是 O(g(n)) , 那 么 d(n)e(n) 是 O(f(n)g(n)) —— 乘法法则

C4

分治法基本思想:当问题规模比较大而无法直接求解时,将原始问题分解 为几个规模较小、但类似于原始问题的子问题,然后递归地求解这 些子问题,最后合并子问题的解以得到原始问题的解。

分治策略遵循三个步骤:

  1. 分解
  2. 解决:递归地解各个子问题
  3. 合并:将子问题的结果合并成原问题的解

因为分治算法的计算时间表达式往往是递归式,所以要化简递归式,已得到形式简单的限界函数。

化简递归式的方法:之前先做合理简化,通常忽略上下取整函数

T(n)=2T(⌊n/2⌋)+n,猜测其解为 T(n)=O(nlgn)

  • 代入法:
    1. 猜测解的形式
    2. 用数学归纳法找出使解真正有效的常数,证明正确
  • 递归树法
  • 主方法:T(n)=aT(n/b)+f(n),三种情况见书

C5

概率分析:使用关于输入分布的知识或者对其做的假设,然后分析算法,计算出一个平均情况运行时间(有不同输入,对每一个输入不管运行多少次算法有个固定的结果,确定型算法)

  • 平均运行时间是算法对所有可能情况的期望运行时间,与输入数据的概率分布有关

随机算法:如果一个算法的行为不仅由输入决定,而且也由 一个随机数生成器产生的数值决定,则称这个算法是随机化 的

  • 我们将一个随机算法的运行时间称为期望运行时间

均匀随机排列就是 n!个排列

指示器随机变量

  • 定义:给定一个样本空间S和一个事件A,那么事件A对应 的指示器随机变量 I{A} 定义为:I{A}=1(如果A发生),0(如果A不发生)
  • 目的:为 了 建 立 概 率 和期望 之间的联系,用于 实现概率与期望之间的转换

C6-9

堆排序算法:

  • O(nlgn)
  • 原址排序算法

最大堆:除 根 节 点 之 外 的 每 个 节 点 i , 有 A[PARENT(i)] ≥ A[i],即某结点的值不大于其父结点的值,故 堆中的最大元素存放在根结点中

最小堆:除 根 节 点 之 外 的 每 个 节 点 i , 有 A[PARENT(i)] ≤ A[i],即某结点的值不小于其父结点的值,故 堆中的最小元素存放在根结点中

维护最大堆操作:

  • 传入参数数组A和下表i,假定 以LEFT(i)和RIGHT(i)为根的两棵二叉树都是最大堆

  • 运行时间为 O(lgn),维护最大堆性质的关键

  • 设一个变量largest,将A[i]和A[LEFT(i)]比较,大者下标存入largest,将A[largest]和A[RIGHT[i]]比较,大者存入largest;如果largest不等于i,交换A[i]和A[largest]

建堆操作:

  • for i = ⌊A.length / 2⌋ downto 1:MAX-HEAPIFY(A, i)
  • 对最后一个叶节点的根结点之前的根节点都进行一次维护操作
  • O(n)

堆排序算法:

  • O(nlgn)

  • 先将A建成一个最大堆

  • 将A[1]和A[n]互换位置,

  • 去掉结点n

  • 维护堆

  • 重复

快速排序:

  • 原址排序算法
  • 最坏情况运行时间为Θ(n ^2 ),期望运行时间为O(nlgn)
  • 对排好序的数组,复杂度为O(nlgn)

过程:选最后一个元素为基准值,按基准值把数组分为小于基准值和大于基准值两个数组,分别对这两个数组继续进行快速排序,i,j,当j遇见比基准值小的,将其和i+1交换

计数排序:

  • 如果所有待排序元素均为0到k之间的整数,则当k=O(n), 时间复杂度:Θ(n+k)
  • 基本思想:对每一个输入元素 x,统计出小于 x 的元素的个 数。然后,根据这一信息直接把元素 x 放到它在最终输出数 组中的位置上
  • 熟练掌握伪代码[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c6ULW5X1-1616301930287)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20210111091653273.png)]
  • 伪代码步骤:
    1. 创建C数组,用来保存等于i的元素的个数,将C数组元素都初始化为0
    2. 遍历A数组,保存等于i的元素的个数
    3. 遍历C数组,保存小于或等于i的元素的个数
    4. 把每个元素 A[j] 放到输出数组B中正确的 位置,决定了计数排序的稳定性

桶排序:

  • 熟练掌握伪代码 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cyCrTA4d-1616301930288)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20210111094740863.png)]
  • O(n),最坏0(n^2)
  • 步骤:
    • 准备 n 个桶(bucket),B[1…n],将元素 x 依照 x 所在的区间放进 对应的桶中:即第 ⌈nx⌉ 个桶;
    • 元素放进桶时,使用链表来存储,并利用插入排序法排序
    • 只要依序将链表串接起來,即得到已排序的 n 个元素

C10-14

栈和队列:

栈实现的是一种后进先出(LIFO)策略的动态集合:S.top

队列实现的是一种先进先出(FIFO)策略的动态集合:Q.head,Q.tail

链表:

  • 实现的是一种线性顺序排列的对象集合
  • 用一组任意的存储单元存储线性表的数据元素(关键字),同时 还存储下一个结点地址(指针),这两部分信息组成为结点

双向链表的每个元素包含关键字和两个指针:next 和 prev

链表的搜索:从head开始,往后搜索,最坏O(n)

双向链表的插入:O(1)

  1. x.next=L.head
  2. 如果原先链表有元素,则将L.head.prev=x
  3. L.head=x
  4. x.prev=NIL

双向链表的删除:

  1. 如果删除结点prev!=NIL,x.prev.next = x.next
  2. 否则说明是头结点,则head=x.next
  3. 如果x.next不是空,x.next.prev = x.prev

散列表:

  • 在元素的关键字与其存储位 置间建立直接联系,以实现直接快速查找
  • 使用散列方法进行查找时,对元素的关键字进行散列函数的计算,把 求得的函数值当做元素存储位置,不必进行多次关键字的比较,查找 速度比较快

解决冲突:

  1. 链接法:将同一个槽的所有元素放在一个链表中。
  2. 开放寻址法: 通过探查确定序列,依次遍历所有表项,直到有空位插入,满了就不能插入了。序列即是1-n的一个排列

线性探查:ℎ(𝑘, 𝑖) = (ℎ′(𝑘)+𝑖) mod m 弄清除k和i是什么,k是要插入的值

二次探查:ℎ(𝑘, 𝑖) = (ℎ′(𝑘)+c1 * 𝑖+c2 * i ^2 ) mod m

上两个方法都有 群集现象:如果两个关键字的初始探 查位置相同,那么它们的探查序列也是相同的

双重散列:ℎ(𝑘, 𝑖) = (ℎ1 (𝑘)+𝑖h2 (k)) mod m

每个后面都有mod m

二叉搜索树:

性质:

  • 若其左子树非空,则左子树上所有结点的关键字都小于根结 点的关键字
  • 若其右子树非空,则右子树上所有结点的关键字都大于根结 点的关键字
  • 左子树和右子树也是二叉搜索树

查询操作:O(h),h是树的高度

返回最大元素操作:O(h)

返回最小元素操作:O(h)

后继:O(h)

  • 有右子树,则后继节点是右子树中关键字最小的结点
  • 若一个结点没有右子树:
    • 若该结点是其父结点的左边孩子,那么该结点的后继结点即 为其父结点
    • 若该结点是其父结点的右边孩子,那么需要沿着其父亲结点 一直向树的顶端寻找,直到找到一个结点 P,P 结点是其父 结点 Q 的左边孩子,那么 Q 就是该结点的后继结点

插入操作:不断比较直到找到一个合适的叶节点,再次比较决定插入到哪一个,O(h)

删除操作:见书

红黑树:

平衡树:左右两个子树高度差不超过一

红黑树性质:

  • 根节点是black
  • 叶节点是black
  • 如果一个节点是red,则两个子节点是black
  • 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含 相同数目的 Black 结点-------------------->得出这条路径上的black结点个数称为黑高
  • 一棵有 n 个内部结点红黑树的高度至多为 2lg(n+1)。
  • 一般操作都可以在O(lgn)内完成

旋转:看代码

插入删除操作:可能会破坏的性质是

  • 根节点为黑色
  • 如果一个节点是red,则两个子节点是black

C15

思想:分解成子问题,然后合并,形成解。与分治法不同,适用于有子问题重叠的情况,即不同的子问题 具有公共的子子问题。

相同点:都是将待求解问题分解成 若干个独立子问题,然后将子问题的解进行合并,形成原问题的解

不同点:分治法要求将问题划分为一些独立不重叠子问题,子问题之间 不包含公共的子子问题,而动态规划适用于有子问题重叠的情况,即不同的子问题 具有公共的子子问题,对相同子问题只求解一次

用来求解的问题:最优化问题——通常有多个可行解,我们要求其最优解

步骤:

  1. 刻画一个最优解的结构特征—>划分子问题
  2. 递归地定义最优解的值 ---->给出最优解的递归式
  3. 按自底向上的方式计算最优解的值
  4. 由计算出的结果构造一个最优解

掌握递归求解公式

排序

稳定:

不稳定:

原址排序:

选择排序O(n^2):每次都在数组选出最大或最小元素(python直接列表pop,插入新列表

c++每次和arr[0]、arr[1]、arr[2]……交换)

插入排序O(n^(1-2)):从第二个元素开始,每次将当前元素和这个元素之前的元素一一比较,小则交换,大则停止。最后得到一个升序

归并排序(O(nlgn)):先对半分组,分到不能再分之后,再合并成有序数组(两组从头开始遍历,小的数就进合并数组,继续一个个比较,最后一个值设置为很大的数,作为哨兵)

快速排序:选第一个元素为基准值,按基准值把数组分为小于基准值和大于基准值两个数组,分别对这两个数组继续进行快速排序

复习卷答案:

分治模式在每层递归时都有三个步骤:分解、解决、合并

堆排序的平均时间复杂度为O(nlgn),存储结构是:数组

矩阵乘法Strassen算法应用了 分治(递归?)算法设计方案,渐进复杂性为Θ(n^lg7)

大则停止。最后得到一个升序

归并排序(O(nlgn)):先对半分组,分到不能再分之后,再合并成有序数组(两组从头开始遍历,小的数就进合并数组,继续一个个比较,最后一个值设置为很大的数,作为哨兵)

快速排序:选第一个元素为基准值,按基准值把数组分为小于基准值和大于基准值两个数组,分别对这两个数组继续进行快速排序

复习卷答案:

分治模式在每层递归时都有三个步骤:分解、解决、合并

堆排序的平均时间复杂度为O(nlgn),存储结构是:数组

矩阵乘法Strassen算法应用了 分治(递归?)算法设计方案,渐进复杂性为Θ(n^lg7)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值