1.1 什么是算法
1.1.1 算法的定义
定义:算法是对特定问题求解步骤的一种描述,是指令的有序序列
1.1.1 算法的三大特性(判断是不是算法)
(1)有穷性
算法能够在有限的时间内执行完所有指令
(2)确定性
每条指令是确定的,也就是相同的输入,只能得到相同的输出
(3)可行性
算法中的每一条指令都可以通过执行有限次的且已经实现的基础操作实现(可实现)
eg:这是算法吗?
例1.1 设计算法求两个自然数的最大公约数
【想法】48=2×2×2×2×3,36=2×2×3×3
则公因子有2、2、3,因此,最大公约数为2×2×3=12
【"算法"】设两个自然数是m和n,"算法"如下
步骤 1:找出 m 的所有质因子
步骤 2:找出 n 的所有质因子
步骤 3:从第 1 步和第 2 步得到的质因子中找出所有公因子
步骤 4:将找到的所有公因子相乘,结果即为 m 和 n 的最大公约
答案是:不是
因为第一步和第二步,不满足确定性,解释:因为没有明确定义怎么去找到一个自然数的全部质因数.
第三步怎么找的指令不知道怎么执行,不满足可行性。(这部分理解不够,还望指教,不过总体是对的)
1.1.2 算法的描述方法
1、自然语言
2、流程图
3、程序设计语言
4、伪代码(主要)
1.13算法在问题求解中的地位
利用计算机解决问题最重要的一步是:将人的想法描述成算法
1.2 什么是好的算法
1.2.1 如何评价算法(五个方面)
1、正确性
2、健壮性
3、可理解性
4、抽象分级
5、高效性
1.2.2效率——算法的核心和灵魂
算法研究的核心问题是速度(时间)问题
1.4.2重要的问题类型(六大问题类型)
1、查找问题
2、排序问题
3、图问题
4、组合问题
5、数学问题
6、几何问题
1.4.3算法设计的一般过程
1、分析问题
2、选择算法设计 技术
1、最优化问题:贪心法、动态规划法
2、迷宫类问题:暴力搜索法、回溯法
3、数据结构为树时:分治-递归法
4、NP类问题:近似算法、概率算法、群智算法
3、设计并描述算法
4、手工并描述算法
发现算法逻辑错误的重要方法
5、分析算法的效率
6、实现算法
1.5.2 算法优化技巧
优化代码的前提
:是算法优化。如果算法本身效率不高,即使优化了也无济于事
1、常量计算
2、算术运算
(1)用加和减来代替乘和除。例如:“3 * x”可修改为“x + x + x”。
(2)高次整幂采取降阶操作。例如:“a*x4+b*x3+c*x2+d*x+e”可修改为“ ((((a*x+b)*x)+c)*x+d)*x+e”。降阶还可以防止中间项的值过大或过小造成溢出
3. 用位运算代替除法和取模运算。理论上可以用位运算完成所有操作。灵活的位运算可以有效提高程序运行的效率,例如,将判断变量x是否为奇数的表达式“x % 2 != 0”修改为“(x & 1) == 1”。
4. 避免重复计算。程序中相同的运算最好计算一次并暂存起来,以后直接使用中间结果。这样做虽然增加了一个中间变量,但实际编译时编译器也会为运算结果分配中间存储单元,实际上并不会增加内存开销。
5. 有利于编译优化。对于表达式“x = a - b”和“y = b - a”,等号右侧表达式的绝对值是一样的,但编译器不会识别出来,会为这两个表达式分别安排存储单元,分配操作指令,可以修改为“x = a - b”和“y = -x”。
6. 优化逻辑运算。所谓短路指的是自左向右计算,一旦可以得到表达式的结果就跳出表达式的计算。因此要合理组织逻辑运算,消除冗余判断。例如:
(1)(i >= 0 && a[i] != x),先计算i >= 0的值,若为真,再计算下一个表达式,若为假,则整个表达式的值为假,余下的表达式就不进行计算了;
(2)(i >= 0 || a[i] != x),先计算i >= 0的值,若为假,再计算下一个表达式,若为真,则整个表达式的值为真,余下的就不再做了。
7. 合理安排条件表达式的顺序。对于嵌套的分支语句,一般按照表达式成立的概率安排分支语句,将执行概率较大的条件表达式放在前面。
8. 改善循环结构。一般来说,程序的执行时间主要耗费在循环结构上,因此提高循环结构的执行效率会产生累积效应。
(1)不滥用循环。由于执行循环语句有赋初值、判断和增值等内存开销,因此能用表达式实现的功能尽量不用循环来实现
(2)合理安排嵌套循环。在不影响程序逻辑的情况下,将循环次数较多者作为内循环(即小循环套大循环)
(3)合并循环。循环执行一次的时间开销较大,因此要避免冗余循环,循环体执行一次完成尽可能多的工作。
(4)循环不变式外提。重复计算是循环中最常见的情况,特别是在内循环中,多写一个冗余操作整个执行结果就会增加上百个冗余操作。将与循环变量无关的操作(称为循环不变式)提到循环外面,可以大大提高代码的效率,例如:
(5)循环无开关。循环体中如果出现与循环变量无关的判断,则可以在循环外面进行判断,例如下面的程序段,虽然修改后的程序段中有两个循环,但实际上只执行1个,而且还少了99次判断: