算法:
算法是解决特定问题 求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。
2.2数据结构与算法:
数据结构与算法的关系是相互依赖不可分割的。
2.3两种算法的比较:
求1+2+3+......+100
(1)
int i, sum = 0, n = 100;
for(i = 1; i < n; i ++)
{
sum += i;
}
printf("%d", sum);
(2)(高斯的做法)
int i,sum = 0, n = 100;
sum = (1 + n) * n / 2;
printf("%d", sum);
2.4算法的定义:
为了解决某个或某类问题,需要把指令表示成一定的操作序列,操作序列包括一组操作,每个操作都完成特定功能,这就是算法了。
2.5算法的特性:
1.输入和输出
2.有穷性 3. 确定性 4.可行性
2.6算法的设计要求:
1.正确性 2.可读性 3. 健壮性 4.时间效率高和存储量低
算法的“正确性”通常在用法上有很大的差别,大体分为以下四个层次:
(1)算法程序没有语法错误;
(2)算法程序对于合法的输入数据能够产生满足要求的输出结果;
(3)算法程序对于非法的输入数据能够得出满足规格说明的结果;
(4)算法程序对于精心选择的,甚至刁难的测试数据都有满足的输出结果(最困难,我们几乎不可能逐一验证所有的输入都得到正确的结果)。
算法的正确性在大部分情况下使用数学方法证明的。
时间效率:
指算法的执行时间。
存储量需求:
指算法在执行过程中需要的最大存储空间,主要指算法程序运行时所占的内存或外部硬盘存储空间。
2.7算法效率的度量方法:
2.7.1事后统计方法:
通过设计好的测试程序和数据,利用计算机计时器对不同算法的程序运行时间进行比较,从而确定算法效率的高低。
存在缺陷:
(1)必须依据算法事先编制好的程序,这通常需要花费大量的时间和精力。
(2)时间比较依赖计算机的硬件和软件等环境因素,有时会掩盖算法本身的优劣。
(3)算法的测试数据设计困难,并且程序的运行时间往往海域测试数据的规模有很大关系,效率高的算法在小的测试数据面前往往得不到体现。
所以,这种方法不予采纳。
2.7.2事前分析估算方法
在计算机程序编制前,依据统计方法对算法进行估算。
一个高级程序语言编写的程序在计算机上运行时所消耗的时间取决于下列因素:
(1)算法采用的策略、方法(算法好坏的根本);
(2)编译产生的代码质量(由软件来支持);
(3)问题的输入规模;
(4)机器的执行指令的速度(看硬件性能)。
抛开这些与计算机硬件软件有关的因素,一个程序的运行时间,以来与算法的好坏和问题的输入规模。所谓问题的输入规模是指输入量的多少。
在分析算法的运行时间时,把基本操作的数量与输入规模关联起来,即基本操作的数量必须表示成输入规模的函数。
2.8函数的渐进增长:
输入规模n在没有限制的情况下,只要超过一个数值N,这个函数就总是大于零一个函数,我们称函数是渐进增长的。
定义:给定两个函数f(n)和g(n),如果存在一个整数N,使得对于所有的n>N,f(n)总是比g(n)大,那么我们说f(n)的增长渐近快于g(n).
如果对比几个算法的关键执行次数函数的渐近增长性,基本就可以分析出:某个算法,随着n的增大,它会越来越优于另一算法,或者越来越差于另一算法。
这其实就是事前估算方法的理论依据,通过算法时间复杂度来估算算法时间效率。
2.9算法时间复杂度
2.9.1算法时间复杂度的定义:
在进行算法分析时,语句的总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度,也就是算法的时间量度,记作T(n)=O(f(n))。它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐近时间复杂度,简称时间复杂度。其中f(n)是问题规模n的某个函数。
2.9.2推导大O阶方法:
(1)用常数1取代运行时间中的所有加法常数;
(2)在修改后的运行次数函数中,只保留最高项;
(3)如果最高项存在且不是1,则去除与这个项相乘的常数。
得到的就是大O阶。
2.9.3常数阶
与问题的大小无关(n的多少),执行时间恒定的算法,我们称之为具有O(1)的时间复杂度,又叫常数阶。
单纯的分支结构:O(1)。
2.9.4线性阶
要分析算法的复杂度,关键是要分析循环结构的运行情况。
2.9.5对数阶
int count = 1;
while (count < n)
{
count = count * 2;
}
由于每次count乘以 2以后,就距离n更近了一分。也就是说有多少个2 相乘以后大于n,则会退出循环。2^x = n,得到x = log2n.
所以这个循环时间复杂度:O(log n)。
2.9.6平方阶
O(n ^2)
循环的时间复杂度 = 循环体的复杂度 * 该循环运行的次数。
理解大O阶推导并不难,难的是对数列的一些相关运算,这更多的是考察数学知识和能力。
还有,对于方法调用的的时间复杂度,如何分析?
2.10 常见的时间复杂度
常数阶 O(1),线性阶 O(n), 平方阶 O(n ^ 2), 对数阶 O(log n), nlogn阶, 立方阶O(n ^ 3), 指数阶 O(2 ^ n)。
所耗费时间从小到大:
O(1) < O(log n) < O(n) < O(nlogn) < O(n ^ 2) < O(2 ^ n) < O(n !) < O(n ^ n)。
2.11最坏情况与平均情况
最坏情况运行时间是一种保证,那就是运行时间将不会再坏了。在应用中,这是一种最重要的需求,通常,除非特别指定,我们提到的运行时间都是最坏情况的运行时间。
平均运行时间是所有情况中最有意义的因为它是期望的运行时间。
2.12算法空间复杂度
我们在写代码时,完全可以用空间来换取时间。算法的空间复杂度通过计算所需的存储空间实现,算法空间复杂度的计算公式记作:S(n) = O(f(n)),其中,n为问题的规模,f(n)为语句关于n所占存储空间的函数。
一般情况下,一个程序在机器上执行时,除了需要存储程序本身的指令、常数、变量和输入数据外,还需要存储对数据操作的存储单元。