第8章 高效算法设计读书笔记
2015-05-05 15:43
260 查看
算法分析初步
8.1.1 渐进时间复杂度 ——最大连续和,给出一个序列,找到i,j使连续和尽量大
使用枚举思想:枚举每一个可能的序列首和序列尾
对这个程序进行简化:连续子序列之和等于两个前缀和之差
1、划分问题:划分成子问题 ; 把序列分成个数相等的两半
2、递归问题:递归解决子问题; 分别求出完全位于左半或者右半的最佳序列
3、合并问题:合并子问题的解 求出起点位于左半终点位于右半的序列,并和子问题最优解比较
递归方程 T(n) = 2T(n/2) + O(n); T(1) = 1; 的解是T(n) = O(n log n)
再谈排序和搜索
8.2.1 归并排序 ——也可以用分治法分成三个步骤
1、划分问题:划分成子问题 ; 把序列分成个数相等的两半
2、递归问题:递归解决子问题; 两半分别排序
3、合并问题:合并子问题的解 把两个有序表合并成一个
8.2.2 快速排序 ——暂时略
8.2.3 二分查找 ——折半查找的思想,不过只针对有序序列
即求 a 所在的下界 L 和 b 所在的上界 R ,而后求区间 [ L, R]长度
递归与分治 ——【TBC】
贪心法 ——排序、取当前最优解
8.4.1 最优装载问题 ——装轻的比装重的划算,故将所有物体按重量从小到大排列
8.4.2 部分背包问题 ——部分选取,一定能让总重量恰好为C。
8.4.3 乘船问题 ——两个下表i,j来表示当前最重和最轻的人
If(最轻的人+最重的人 < 标准重) { num++; i++; j--;} //浪费最少
Else { num++; j--;} //此时最重的只能自己乘一条船,往下寻找能和最轻的人同乘的 最重的人
8.4.4 选择不相交区间 —— 有n个区间(Ai, Bi)。如果区间a能够完全覆盖b则选取a就是
不划算的,依据Bi将所有区间排序,那么第一个元素一定是在最优解中
排序后B1 < B2
①A1 >= A2时,区间1被区间2完全包含,所以选取区间2是不划算的
②A1 < A2时,区间1中位于A2左边的部分对结果并没有影响,真正有影响
的是区间1 中A2到B1部分,这一部分被区间2完全包含,所以选取区间2是不划算的
所以应该选取第一个区间,然后需寻找此后与此区间不相交的第一个区间
8.4.5 区间选点问题 ——小区间被满足时大区间也会被满足,所以在区间包含的情况下大区 间不用考虑。依据Bi将所有区间排序,然后取最后一个点
8.4.6 区间覆盖问题 ——进行一个预处理,所有区间在线段[s,t]外的部分都切掉,因为存在 无意义。依据Ai将所有区间排序,如果第一个区间起点不是s,则无解。否则选取最长区间,随后设置此区间尾Bi为新的起点。
8.4.7 Huffman编码 ——证明用Huffman树可以得出最优解需要以下两个结论:
①如果编码a是编码b的前缀,那么a对应的结点一定为b所对应结点的祖先。
而两个叶结点不存在祖先后代的关系
②最有前缀码一定可以写成二叉树
构造一颗最优编码树(Huffman算法):每棵子树权值等于相应字符的频率,每次 选择权值最小的树组成一颗权值为两树之和的新树,放回集合中。满足以下两个性质
①设x,y是使用频率最小的两个字符,存在前缀码使二者具有相同码长,仅有 最后一位不同(保留了最优解)
②设z为x,y的父结点,把z看成频率具有x,y频率之和的字符,那么此树是
包含z而无x,y字符的集合的最优解(最优子结构性质)
8.1.1 渐进时间复杂度 ——最大连续和,给出一个序列,找到i,j使连续和尽量大
使用枚举思想:枚举每一个可能的序列首和序列尾
for( i = 1; i <= n; i++){ for( j = i; j <= n; j++){ int sum = 0; for( k = i; k <= j; k++){ // 求和 sum += a[k]; tot++; // 计算基本操作的数量,T(n)和n的3次方同阶 } if( sum > best) best = sum; } }8.1.2 上界分析 ——三重循环,最坏情况下内层循环需要n次,故T(n) = O(n的3次方)(上界)
对这个程序进行简化:连续子序列之和等于两个前缀和之差
for( i = 1; i <= n; i++){ for( j = i; j <= n; j++){ best = max( s[j] - s[i-1], best); // 遍历数组,更新best值,T(n)和n的2次方同阶 } }8.1.3 分治法 ——分治法一般分为3个步骤
1、划分问题:划分成子问题 ; 把序列分成个数相等的两半
2、递归问题:递归解决子问题; 分别求出完全位于左半或者右半的最佳序列
3、合并问题:合并子问题的解 求出起点位于左半终点位于右半的序列,并和子问题最优解比较
递归方程 T(n) = 2T(n/2) + O(n); T(1) = 1; 的解是T(n) = O(n log n)
再谈排序和搜索
8.2.1 归并排序 ——也可以用分治法分成三个步骤
1、划分问题:划分成子问题 ; 把序列分成个数相等的两半
2、递归问题:递归解决子问题; 两半分别排序
3、合并问题:合并子问题的解 把两个有序表合并成一个
#include<stdio.h> void sort( int* a, int x, int y, int* t); int a[] = { 20, 12, 13 }; int t[100]; int main(void){ int i; sort( a, 0, 3, t); // 第三个参数是3确保指向空位置 for( i = 0; i < 3; i++) printf("%d ", a[i]); return 0; } void sort( int* a, int x, int y, int* t){ int m, p, q, i; if( y-x > 1){ // xy之间序列不为空 m = x + (y-x) / 2; // 划分 p = x, q = m, i = x; // 三个指针 // 事实上p和q都好理解,i = x 可以理解为这是将由x为起点的数组重新排序 sort( a, x, m, t); // 左递归 sort( a, m, y, t); // 右递归 while( p < m || q < y){ // 序列非空、继续合并 if( q >= y || ( p < m && a[p] <= a[q])){ /* q >= y : 右序列为空(此时左序列不为空) 或 p < m && a[p] <= a[q]) p < m : 左序列不为空 且 a[p] <= a[q] : 左小于右 (此时认为p为左序列最小值,而q为右序列最小值) */ t[i++] = a[p++]; // 把P赋值给t[i],并比较下一个元素 } else{ /* 右序列不为空 或 左大于右 */ t[i++] = a[q++]; // 把P赋值给t[i],并比较下一个元素 } } for( i = x; i < y; i++){ // 赋值回函数 a[i] = t[i]; } } }逆序对数(尚没有仔细看懂)
8.2.2 快速排序 ——暂时略
8.2.3 二分查找 ——折半查找的思想,不过只针对有序序列
#include<stdio.h> void sort( int* a, int x, int y, int* t); // 利用之前的分治方法进行排序 int main(){ int a[9] = { 20, 12, 13, 18, 19, 4, 7, 16, 5}; int t[100]; int x, y, m, v, i, num; scanf("%d", &m); sort( a, 0, 9, t); for( i = 0; i < 9; i++) printf("%d ", a[i]); printf("\n"); x = 0; y = 8; num = 0; while( x < y){ // 一般二分查找写成非递归形式 v = x + (y-x) / 2; if( a[v] == m){ printf("序号是%d\n", v+1); break; } else if( a[v] <= m) x = v; else y = v; } return 0; } void sort( int* a, int x, int y, int* t){ int m, v, i, p, q; if( y - x > 1){ m = x + (y-x) / 2; sort( a, x, m, t); sort( a, m, y, t); p = x; q = m; i = x; while( p < m || q < y){ if( q >= y || ( p < m && a[p] < a[q])) t[i++] = a[p++]; else t[i++] = a[q++]; } for( i = x; i < y; i++) a[i] = t[i]; } }如果序列中有重复,且重复的为所求数字,那么用原来的二分法就只能求得最中间的位置,需要进行一些小的修改
while( x < y){ // 二分查找求上界 v = x + (y-x) / 2; if( a[v] <= m){ x = v+1; // 此时这里很重要,否则会发生死循环 } else y = v; // 若求下界则是当相等时改变y }范围统计:给出一个序列,以及m个询问,对于每个询问(a, b),求闭区间 [a,b] 内个数
即求 a 所在的下界 L 和 b 所在的上界 R ,而后求区间 [ L, R]长度
递归与分治 ——【TBC】
贪心法 ——排序、取当前最优解
8.4.1 最优装载问题 ——装轻的比装重的划算,故将所有物体按重量从小到大排列
8.4.2 部分背包问题 ——部分选取,一定能让总重量恰好为C。
8.4.3 乘船问题 ——两个下表i,j来表示当前最重和最轻的人
If(最轻的人+最重的人 < 标准重) { num++; i++; j--;} //浪费最少
Else { num++; j--;} //此时最重的只能自己乘一条船,往下寻找能和最轻的人同乘的 最重的人
8.4.4 选择不相交区间 —— 有n个区间(Ai, Bi)。如果区间a能够完全覆盖b则选取a就是
不划算的,依据Bi将所有区间排序,那么第一个元素一定是在最优解中
排序后B1 < B2
①A1 >= A2时,区间1被区间2完全包含,所以选取区间2是不划算的
②A1 < A2时,区间1中位于A2左边的部分对结果并没有影响,真正有影响
的是区间1 中A2到B1部分,这一部分被区间2完全包含,所以选取区间2是不划算的
所以应该选取第一个区间,然后需寻找此后与此区间不相交的第一个区间
8.4.5 区间选点问题 ——小区间被满足时大区间也会被满足,所以在区间包含的情况下大区 间不用考虑。依据Bi将所有区间排序,然后取最后一个点
8.4.6 区间覆盖问题 ——进行一个预处理,所有区间在线段[s,t]外的部分都切掉,因为存在 无意义。依据Ai将所有区间排序,如果第一个区间起点不是s,则无解。否则选取最长区间,随后设置此区间尾Bi为新的起点。
8.4.7 Huffman编码 ——证明用Huffman树可以得出最优解需要以下两个结论:
①如果编码a是编码b的前缀,那么a对应的结点一定为b所对应结点的祖先。
而两个叶结点不存在祖先后代的关系
②最有前缀码一定可以写成二叉树
构造一颗最优编码树(Huffman算法):每棵子树权值等于相应字符的频率,每次 选择权值最小的树组成一颗权值为两树之和的新树,放回集合中。满足以下两个性质
①设x,y是使用频率最小的两个字符,存在前缀码使二者具有相同码长,仅有 最后一位不同(保留了最优解)
②设z为x,y的父结点,把z看成频率具有x,y频率之和的字符,那么此树是
包含z而无x,y字符的集合的最优解(最优子结构性质)
相关文章推荐
- 第8章:高效算法设计
- 【索引】算法竞赛入门经典-第8章 高效算法设计
- 基于内容的推荐系统---《推荐系统技术、评估及高效算法》---读书笔记(3)
- 开发基于约束条件的推荐系统---《推荐系统技术、评估及高效算法》---读书笔记(6)
- 高效算法设计专项:UVa 10730
- 高效算法设计专项:UVa 10535
- 高效算法设计
- 高效算法设计专项:UVa 10827
- 紫书_第八章_高效算法设计_8.3.2——循环日程表问题
- 高效算法设计专项:LA 2689
- 【高效算法设计——滑动窗口】 UVa 12174 Shuffle
- 算法设计手册(第2版)读书笔记, Springer - The Algorithm Design Manual, 2ed Steven S.Skiena 2008
- 算法竞赛入门经典:第八章 高效算法设计 8.2归并排序
- 转:A、B两个整数集合,设计一个算法求他们的交集,尽可能的高效。
- 【读书笔记】简约至上--交互设计四策略--第8章 最后的叮嘱
- SEERC 2006 Subsequence, 高效算法设计 ,LA 2678
- A,B两个整数集合,设计一个算法求他们的交集,尽可能的高效
- 集训第四周(高效算法设计)F题 (二分+贪心)
- 算法设计手冊(第2版)读书笔记, Springer - The Algorithm Design Manual, 2ed Steven S.Skiena 2008
- [高效算法设计]Calculator conundrum Uva 11549