和小白一起学数据结构十一之算法分析

数据结构之算法分析

由于学校课业的压迫,博主完成每日一更的flag倒了个彻底哈哈哈,上周复变函数结课,周末也终于有时间可以写一写搁置这么多天的博客。

前两周的复变函数课程分别上了傅立叶变换、卷积定理和拉普拉斯变换,是工程中非常实用的积分变换知识。等博主时间更宽裕些了,就相应更一些关于此类工程问题的数学知识储备和计算机工程应用、电路三相异步和RC稳态方面与计算机底层联系的知识博客,就当作为断更这么多天对各位道友内心巨大歉疚的补偿~

今天我们来系统梳一遍算法分析1。数据结构课内已经上到二叉树的查找算法,实验课也已经过了一半,博客拉下的知识和进度之后会抽时间慢慢补上。
话不多说,开始吧!

一、为什么要做算法分析

  • 算法(Algorithm)是为求解一个问题需要遵循的、被清楚指定的简单指令集合。对于一个问题,一旦给定某种算法并且确定其为正确的,那么重要的一步就是确定其运行需要多少诸如时间资源空间资源之类的问题。
  • 今天我们将讨论以下问题:
    1.如何估计一个程序所需要的时间
    2.如何优化一个程序的运行效率

二、数学基础

估计算法的资源消耗所需要的资源分析一般而言是理论问题,因此需要一套正式的系统构架,我们先从几个数学定义开始:

  • 定义一:如果存在一个正常数c和n0使得当N>=n0时T(N)<=cf(n),则记为T(N)=O(f(n)).
  • 定义二:如果存在一个正常数c和n0使得当N>=n0时T(N)>=cg(n),则记为T(N)=Ω(g(n)).
  • 定义三:T(N)=Θ(h(n)),当且仅当T(N)=O(f(n)),T(N)=Ω(g(n)).
  • 定义四:如果T(N)=O(p(N))且T(N)!=Θ(h(n)),则T(N)=o(N).(不同于大O,因为大O包含增长率相同这种可能性)

这些定义的目的在于要在函数间建立一种相对级别,就是长度尺寸的度量比较标准由于c为正常数及函数关系为线性关系,所以我们可以将以上比较标准简化理解为,关于函数增长率的比较。

基于以上定义,我们还需掌握的结论:

  • 法则一:
    如果T1(N)=O(f(N))且T2(N)=O(g(N)):
    1. T1(N)+T2(N)= max( O(f(N)),O(g(N)) )
    2. T1(N)*T2(N)= O( f(N)*g(N) ).
  • 法则二:
    如果T(N)是一个k次多项式,则T(N)=Θ(Nk).
  • 法则三:
    对于任意常数k,logkN=O(N),由此可知对数增长缓慢

这些信息足以按照增长率对大部分常见函数进行分类(见表)

函数名称
c常数级
log N对数级
log2 N对数平方根
N线性级
N2平方级
N3立方级
2N指数级

注意:
1.将常数或者低阶项放进O是个非常坏的习惯。
2.我们通常可以通过计算两个函数比值的极限来确定其相对增长率,必要的时候可以使用洛必达法则(L’Hopital’s rule).极限有四种可能的值:

f(N)/g(N)极限值内涵
0f(N)=o(g(N))
f(N)=Ω(g(N))
cf(N)=Θ(g(N))
不存在极限摆动二者无关

三、模型

为了在正式的框架下分析算法,我们需要一个计算模型。
我们的模型基本上是一台标准的计算机,在机器中指令被顺序执行,不能在同一个单位时间内完成不同工作步骤。我们还假设模型机有无限内存。

四、要分析的问题

要分析的最重要资源一般是运行时间,当然一些书籍,比如国内清华大学严蔚敏版的教材同时兼具空间和时间复杂度分析,但基于内存问题相较时间问题更易解决,更易计算,再加上今天我们使用的模型机是内存无限的模型机所以我们重点讨论运行时间问题。
余下的主要因素是所使用的算法及对该算法的输入。 我们定义两个函数TAVg(N)和Tworst(N),分别为输入N时,算法所花费的平均运行时间和最坏情况下的运行时间。
作为一个🌰,我们将在下一节考虑最大的子序列求和问题,这个问题所以有吸引力,主要是因为求解它有很多种算法,而这些算法的性能相差很大。

五、一般法则

法则一 :for循环

一次for循环的运行时间至多是该for循环内的语句(包括测试)的运行时间乘以迭代的次数

法则二 :嵌套的for循环

该组所有for循环的乘积

法则三 :顺序语句

将各个语句的运行时间求和即可
作为一个🌰,下面的程序片段先去O(N),再花费O(N2),总的开销也是O(N2):

for(i=0;i<N;i++)
  A[i]=0;
for(i=0;i<N;i++)
  for(j=0;j<N;j++)
  A[i]=A[j]+i+j;
法则四 :IF/ELSE 语句

对于程序片段:

if(condition)
   S1
else
   S2

一个if/else语句的运行时间从来不超过判断再加上S1和S2中运行时间长者的总的运行时间。

六、运行时间中的对数

分析算法最混乱的方面大致集中在对数上面。

我们接触到的第一个O(logN)时间运行的算法大概是分治算法,下次博主附讲最大子序列求和问题时会提供四个不同的经典算法,第三种算法思想即分治策略(divide-and-conquer),对运行时间的分析方法与在计算斐波那契数列程序时的方法相似。

一般法则:如果一个算法用常数时间将问题的大小削减为其一部分(通常为1/2),那么该算法就是O(logN).

显然,只有一些特殊种类的问题才能呈现此种类型,我们提供具有对数特点的三个例子:

  • 对分查找
    给定一个整数X和整数A0,A1,A2,A3…AN-1后者已预先排序并在内存中,求使得Ai=X的下标i;如果不在,则返回i=-1.代码如下:
int BinarySearch(const Elemtype A[],Elemtype x,int N)
{
  int low,Mid,High;
  low=0;High=N-1;
  while(Low<=High)
  {
    Mid=(low+High)/2;
    if(A[Mid]<x)
       low=Mid+1;
    else 
    if(A[Mid]>x)
      High=Mid-1;
    else 
    return Mid;//找到情况
   }
   return -1;//未找到情况
 }
  • 欧几里得算法
    两个整数的最大公约数是同时整除二者的最大整数。迭代法计算,代码如下:
unsigned int GCD(unsigned int M,unsigned N)
{
  unsigned int Rem;
  while(N>0)
  {
    Rem=M%N;
    M=N;
    N=Rem;
   }
   return M;
}
  • 幂运算

处理一个整数的幂,高效率取幂运算代码如下:

long int Pow(long int x,unsigned int n)
{
  if(n==0)
    return 1;
  if(n==1)
   reuturn x;
  if(IsEven(n))
    return Pow(x*x,n/2);
  else
    return Pow(x*x,n/2)*x;
 }

以上,周末愉快!


  1. 参考书籍:《数据结构与算法分析》机械工业出版社 (美)Mark Allen Weiss 著 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值