第二章:初识算法(上)
二(1)算法
定义:算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。
二(2)数据结构与算法的关系
(1)数据结构提供了算法执行所需的数据存储和管理方式。
(2)算法则是对数据结构进行操作的过程。
二(3)算法的特性
- 输入(2)输出(3)有穷性(4)确定性(5)可行性
解释:
- 输入输出:算法的输入可以是零个和多个,算法的输出至少有一个或多个输出。(算法一定要有输出的)
- 有穷性:指算法在执行有限的步骤之后,自动结束而不会出现无限循环,并且每一个步骤在可接受的时间内完成。
- 确定性:算法的每一步骤都具有确定的含义,不会出现二义性。(在一定条件下,只有一条执行路径,相同的输入只能有唯一的输出结果)
- 可行性:算法的每一步都必须是可行的,也就是说,每一步都能通过执行有限次数完成。
二(4)算法设计的要求
- 正确性(2)可读性(3)健壮性(4)时间效率高和存储量低
解释:
1.正确性:算法的正确性是指算法至少应该具有输入、输出和加工处理无歧义性,能正确反映问题的需求,能够得到问题的正确答案。
Tips:“正确”的算法大体分为四个层次:
1.算法程序没有语法错误;
2.算法程序对于合法的输入数据能够产生满足要求的输出结果;
3.算法程序对于非法的输入数据能够得出满足规格要求的结果;
4.算法程序对于精心选择的,甚至刁难的测试数据都有满足要求的输出结果。
2.可读性:算法设计的另一目的是便于阅读、理解和交流。
3.健壮性:当输入数据不合法时,算法也能做出相关处理,而不是产生异常或莫名其妙的结果。
4.时间效率高和存储量低:时间效率高是指算法的执行时间;存储量低指算法在执行过程中需要的最大存储空间,主要指算法程序运行时所占用的内存或外部硬盘存储空间。
二(5)算法效率的度量方法
- 事后统计法
定义:这种设计方法主要是通过设计好的测试程序和数据,利用计算机计时器对不同算法编制的程序的运行时间进行比较,从而确定算法效率高低。
缺点:
- 必须依据算法实现编制好程序,这通常需要花费大量的精力和时间。
- 时间的比较依赖计算机硬件和软件等环境因素,有时会掩盖算法本身的优劣。
- 算法的测试数据设计困难,并且程序的运行时间往往与测试数据的规模有很大关系,效率高的算法在小的测试数据面前往往得不到体现。
- 事前分析估算方法
定义:在计算机程序编制前,依据统计方法对算法进行估算。
一个高级程序语言编写的程序在计算机上运行所消耗的时间取决于下列因素:
- 算法采用的策略和方法;
- 编译产生的代码质量;
- 问题的输入规模;
- 机器执行指令的速度。
第1条是算法好坏的根本,第(2)条要由软件来支持,第(4)条靠硬件性能。也就是说:抛开软件和硬件有关的因素,一个程序的运行时间,依赖于算法的好坏和问题的输入规模。问题输入规模是指输入量的多少。
举例:
两种求和的算法:
第一种:
int i,sum=0,n=100;
for(i=1;i<=n;i++)
{
Sum=sum+i;
}
Printf(“%d”,sum);
执行次数:1+(n+1)+n+1=2n+3
第二种:
int sum=0,n=100;
Sum=(1+n)*n/2;
Printf(“%d”,sum);
执行次数:1+1+1=3
二(6)函数的渐近增长
定义:给定两个函数f(n)和g(n),如果存在一个整数N,使得对于所有的n>N,f(n)总是比g(n)大,那么,我们说f(n)的增长渐近快鱼g(n)。
举例:
例一:假设有两个算法:算法A(2n+3),算法B(3n+1)比较两个算法快慢。
次数 | 算法A(2n+3) | 算法A*(2n) | 算法B(3n+1) | 算法B*(3n) |
n=1 | 5 | 2 | 4 | 3 |
n=2 | 7 | 4 | 7 | 6 |
n=3 | 9 | 6 | 10 | 9 |
n=10 | 23 | 20 | 31 | 30 |
n=100 | 203 | 200 | 301 | 300 |
当n=1时,A效率低于B,当n>=2时,A效率比B效率高,于是我们得出结论:算法A总体上好过算法B。
例二:算法C是4n+8,算法D是2n^2+1。
次数 | 算法C(4n+8) | 算法C*(n) | 算法D(2n^2+1) | 算法D*(n^2) |
n=1 | 12 | 1 | 3 | 1 |
n=2 | 16 | 2 | 9 | 4 |
n=3 | 20 | 3 | 19 | 9 |
n=10 | 48 | 10 | 201 | 100 |
n=100 | 408 | 100 | 20001 | 10000 |
n=1000 | 4008 | 1000 | 2000001 | 1000000 |
- 当n<=3时,C要差于D,当n>3后,C比D越来越好,甚至是远远超过;
- 同时,当去掉与n相乘的常数,C比D的结果也没发生改变,也就是说:与最高次项相乘的常数并不重要。
例三:算法E是2n^2+3n+1,算法F是2n^3+3n+1。
次数 | 算法E(2n^2+3n+1) | 算法E*(n^2) | 算法F(2n^3+3n+1) | 算法F*(n^3) |
n=1 | 6 | 1 | 6 | 1 |
n=2 | 15 | 4 | 23 | 8 |
n=3 | 28 | 9 | 64 | 27 |
n=10 | 231 | 100 | 2031 | 1000 |
n=100 | 20301 | 10000 | 2000301 | 1000000 |
- 当n=1时,E与F结果相同,但是当n>1时,E与F的差距越来越大
- 最高次项的指数越大,函数随着n的增长,结果也会增长更快。
例四:算法G是2n^2,算法H是3n+1,算法I是2n^2+3n+1。
次数 | 算法G(2n^2) | 算法H(3n+1) | 算法I(2n^2+3n+1) |
n=1 | 2 | 4 | 6 |
n=2 | 8 | 7 | 15 |
n=3 | 50 | 16 | 66 |
n=10 | 200 | 31 | 231 |
n=100 | 20000 | 301 | 20301 |
n=1000 | 2000000 | 3001 | 2003001 |
n=1000 | 200000000 | 30001 | 200030001 |
n=1000 | 20000000000 | 300001 | 20000300001 |
n=1000 | 2000000000000 | 3000001 | 2000003000001 |
- 当n越来越大,算法H远远小于G和H。
- 判断一个算法的效率时,函数中的常数和其他次要项常常可以忽略,更应该关注主项(最高阶项)的阶数。