算法导论 —— 第一部分 基础知识

第一章 算法在计算中的作用

生活中算法的例子:

  • 最短路径:交通图中从一个路口到另一个路口可能存在多条路线,寻找出路径最短的路线

  • 最长公共子序列:判定两个DNA链的基对序列是否相似,每个序列有 2n个子序列,求最长公共子序列
  • 凸壳:木板上有若干个钉子,凸壳由一根拉紧的环绕所有钉子的橡皮筋来表示,如果橡皮筋因绕过某颗钉子而转弯,那么这颗钉子就是凸壳的一个顶点,寻求最小的凸壳面积

  • 拓扑排序:给出若干个决策方案,我们要对方案进行排序,使得每个方案出现依赖它的方案之前,总共有 n! 种方案。

第二章 算法基础

2.1 插入排序

在打扑克的场景中,考虑当前摸了第 i 张牌,而手上已经排好序的牌为 j = 1...i − 1,此时通过将第 i 张牌倒着与手上已排好序的牌进行比较,来确定当前牌要插入的位置,代码如下:

 
for (int i = 1; i < n; ++i) {    // 当前处理第i张牌
    int key = a[i];            
    int j = i - 1;               // 从第 j = i - 1 张牌开始比较
    while(j >= 0 && a[j] > key) {    // 若第 j 张牌比当前牌的值更大,则继续往前寻找要插入的位置
        a[j + 1] = a[j];
        --j;
    }
    a[j + 1] = key;
}

2.2 分析算法

算法需要的时间与输入的规模同步增长,对长度为1,000的数组排序与对长度为100,000的数组进行排序所需的时间明显是不一样的,所以通常将程序的运行时间描述成其输入规模的函数。

算法的运行时间可以简单看做每条语句的运行时间之和,对于上述的插入排序来说:

  • 最好的情况是数组已经排好序,则 第 4-7行的 while 循环只需执行一次,时间复杂度为θ(n)

  • 最坏情况为数组完全逆序,此时对于每个 i 来说第4 - 7行while循环要执行 i - 1次,忽略常数项以及低阶项,时间复杂度为\theta(n^2)

  • 往往考虑最坏的情况,平均情况通常情况下增长量级和最坏情况是一样的

2.3 分治算法

将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,再合并这些子问题的解来得到原问题的解。

归并排序算法:

  1. 分解:将n个元素的排列分解为 n / 2个元素的子序列排列
  2. 解决:递归解决两个子序列的排序
  3. 合并:合并两个已排序的子序列得到原序列的解
// 将区间 [p, r] 分解为 [p, q], [q + 1, r], 其中 q = (p + r) / 2
[p, q]
[q + 1, r]

merge(A, p, q, r) {
    n1 = q - p + 1;
    n2 = r - q;
    令 L = A[p ... q],R = A[q + 1 .. r]
    int k = p;
    for (int i = 0, j = 0; i < n1 || j < n2; ) {
        if ( (j >= n2 || (i < n1 && L[i] <= R[j]) {  // 若R数组已经被遍历完或R数组中的元素大于L数组中的元素
            A[k++] = L[i++];
        } else {
            A[k++] = R[j++];
        }
    }
}

分治算法的时间复杂度计算:

T(n)=aT(n/b) + D(n) + C(n),其中a表示分解成a个子问题,T(n/b)表示每个规模为 n/b的子问题的求解时间,D(n)表示分解成子问题所需的时间,C(n)表示合并成原问题所需的时间。

将该公式应用到归并排序中:

子问题规模为 n/2,共有2个子问题,合并的时间复杂度C(n)为θ(n),得到 T(n)=2∗T(n/2) + θ(n),可得 T(n)=θ(nlgn)。

逆序对

逆序对的定义为,对于序列 A={a1​, a2​, ..., an​} 来说,存在 i > j 且 a_i<a_j,则可以说存在一组逆序对 (i, j)。

插入排序和逆序对的关系:对于每个处理的数 a[i]来说,它前面有多少个逆序对,2.1节中算法的 while 循环就要执行多少次。

归并排序和逆序对的关系:可以通过归并排序在 θ(nlogn)的时间复杂度内求出逆序对的数量

Horner rule 霍纳规则

霍纳规则用于在线性时间内计算多项式 \sum_{k=0}^na_k*x^k的值,这个式子展开得到 P(x)=a_0+x*a_1+x^2*a_2+...+x^n*a_n=a_0+x(a1+x*(a2+...+x(a_{n-1}+x*a_n)...))

给定数组a以及x,可以写成如下代码:

for (int i = n; i >= 0; --i) {
    y = a[i] + x * y;
}

欢迎关注,持续更新中 ... 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值