《算法设计与分析基础》Chapt 1 介绍
1.1 算法的概念
1. 求最大公约数
欧几里德算法:
gcd(m,n) = gcd(n, m%n)
2. 求2~n的连续素数序列-埃拉托色尼筛(Sieve)
算法:每趟扫描从候选质数中消去已确定是质数的倍数
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | |
2 | 3 | 5 | 7 | 9 | 11 | 13 | 15 | 17 | 19 | 21 | 23 | 25 | ||||||||||||
2 | 3 | 5 | 7 | 11 | 13 | 17 | 19 | 23 | 25 | |||||||||||||||
2 | 3 | 5 | 7 | 11 | 13 | 17 | 19 | 23 |
为了消去p的倍数时,避免多次消去相同的数,例如,我们消去2和3的倍数时按上表都需要消去6
我们注意到:当消去p的倍数时,第一个应考虑的数是pxp, 因2p,...(p-1)p都在前面消去过了
#include <iostream>
#include <cmath>
using namespace std;
void prime_sq(int n)
{
int arr[n-1];
for(int i = 0; i < n-1; ++i)
{
arr[i] = i+2;
}
int j = 0;
while(arr[j] <= sqrt(n))
{
for(int i=arr[j]*arr[j]-2; i < n-1; ++i)
{
if(arr[i]%arr[j] == 0)
arr[i] = 0;
}
while(arr[++j]== 0);
}
for(int i=0; i< n-1; ++i)
{
if(arr[i]!=0)
cout << arr[i] << " ";
}
}
int main()
{
prime_sq(25);
system("PAUSE");
return 0;
}
1.2 算法的求解步骤
1.2.1 理解问题
完全理解问题。
仔细阅读问题的描述,如有任何疑惑就把疑惑提出来,手工处理一些小例子,考虑一下特殊情况,有必要的话再继续提出疑问。
有几类问题频繁出现在计算机应用中,若属于其中的一类,则可以使用已知的算法求解,但必须了解其优缺点。而常常我们无法找到一个完全可用的算法,这就不得不设计我们自己的算法。
1.2.2 了解计算设备的性能
冯.诺伊曼 机器: 随机存取机
指令逐条运行,每次执行一步操作。设计运行于这种机器上的算法成为顺序算法。
而更新的计算机可用在同一时间执行多条操作,即b并行计算。能够利用这种计算性能优势的算法成为并行算法
1.2.3 在精确解法和近似解法间做选择
为什么需要近似解法?
- 有些问题的确无法求得精确解
- 有些问题固有的复杂性,用已知的精确算法来解决会慢得无法接受
1.2.4 确定适当的数据结构
有些算法并不要求输入数据具有精巧的表现形式,而有些算法的确需要基于一些精心设计的数据结构
1.2.5 算法设计技术
算法设计技术(也称为“策略”或“范例”)是用算法解题的一般性方法,用来解决不同计算领域的多种问题。
- 在给新问题设计算法时,他们能够给予指导
- 算法设计技术可以按照内在设计理念对算法进行分类。
1.2.6 详细表述算法的方法
- 自然语言
- 伪代码
1.2.7 证明算法正确性
证明的一般方法是使用数学归纳法,因为算法的迭代过程原本就提供了这种证明所需要的一系列步骤。
证明不正确,只需要提供一个算法不能正确处理的输入实例
近似算法,我们常常试图证明该算法所产生的误差不超过我们预定义的范围。
1.2.8 分析算法
- 效率
- 时间效率
- 空间效率
- 简单性
- 一般性
- 算法所解决问题的一般性
- 算法所接受输入的一般性
1.2.9 算法的程序实现
1.3 重要的问题类型
- 排序
- 查找
- 串处理
- 图问题
- 组合问题
- 几何问题
- 数值问题
1.3.1 排序
- 稳定性
- 如果一个输入列表包含两个相等的元素,他们的位置是i和j, i < j, 在排好序的列表中,他们的位置是i',和j', 如果仍有i' < j',则排序是稳定的
- 额外存储空间
- 除了个别存储单元以外,不需要额外的存储空间,则排序是就地排序(in-place)
1.3.2 查找(搜索)
- 顺序搜索
- 折半搜索 - 效率高但应用受限
- 将原有集合表示为另一种形式 - 大型数据库的信息存储
需要考虑高效查找与删除/插入操作的平衡
1.3.3 串处理
非数值数据的处理:比特串、基因序列
串匹配问题
1.3.4 图问题
图之所以称为一个感兴趣的对象,既有理论上的原因也有实践上的原因。
图可以用来对广泛的、各种各样的实际应用建模,包括交通、通信网络、项目时间表、各种竞赛。
最近一个令人感兴趣的应用是估计internet的直径,即顺着两个网页之间的最短路线会经过的最大链接数。
基本的图算法包括:
- 图的遍历算法(如何一次访问网络中的所有节点?)
- 最短路线算法(两个城市之间的最佳路线是哪条?)
- 有向图的拓扑排序(一系列过程的前提条件是相互一致的还是自相矛盾?)
有些图问题在计算上是非常困难的:
旅行商问题:找出访问n个城市的最短路径,并保证每个城市只访问一遍
图填色问题:用最少种类的颜色为图中的顶点着色,并保证两个邻接顶点颜色不同
1.3.5 组合问题
从更抽象的决度来看,旅行商问题和图填色问题都是组合问题的特例。
有一些问题要求(明确的或者含蓄的)寻找一个组合对象,比如一个排列、一个组合或者一个子集,这些对象能够满足特定的条件并具有我们想要的特性(例如,价值最大化或成本最小化)
无论从理论观点还是实践观点,组合问题斗室计算领域最难的问题:
- 随着问题规模的增大,组合对象的数量增长极快
- 没有一种已知算法能在可接受的时间内,精确地解决绝大多数这类问题。而且,计算机科学家猜想这类算法是不存在的。
但有些算法能用高效的算法解决,应该归功于运气和例外。
1.3.6 几何问题
几何算法应用的领域:计算机图形学、机器人技术、断层X摄像技术等
这里只讨论两个经典的几何问题:
- 最近对问题 - 给定平面上的n个点,求距离最近的两个点
- 凸包问题 - 找一个能把给定集合中所有点包含在里面的最小凸多边形
1.3.7 数值问题
《数值分析》这类教材研究的问题:解方程和方程组、计算定积分、求函数的值等
数值分析丧失了它在计算机科学界和工业界应用的统治地位,但是具有数值算法的基本概念还是非常重要的。
1.4 数据结构
1.4.1 线性数据结构
- 数组
- 链表
1.4.2 图
1. 图的表示
- 邻接矩阵
- 邻接链表
2. 加权图
3. 路径和环
- 简单路径 - 路径上的所有的边都互不相同
- 连通
- 环
4. 树
树就是连通的无回路图。
森林是无回路但不一定连通的图。
树的重要性质:
- 树的边数总是比它的顶点数少1 : |E|=|V|-1
- 自由树->有根树
有根树:树的任意两个顶点之间总是恰好存在一条从顶点到另一个顶点的简单路径。
树的应用:描述层次关系(文件目录,企业的组织架构),字典的实现,超大型数据集合的高效存储,数据编码。
状态空间树->回溯和分支界限
一些概念:
- 祖先
- 父母
- 子女,子孙
- 兄弟
- 叶节点
- 子树
- 顶点的深度:从根到顶点的简单路径的长度
- 树的高度:从根到叶节点的最长简单路径的长度+1
有序树:是一棵有根树,树中的每个顶点的所有子女都是有序的。例如,二叉树
二叉搜索树:
对于高度为h,具有n个顶点的二叉树,有不等式,
1.4.5 集合与字典(map)
集合的运算:
- 检查一个给定项是不是给定集合的成员
- 求两集合的并集
- 求两集合的交集