您的位置:首页 > 理论基础 > 数据结构算法

数据结构与算法分析(chapter2 算法分析)

2011-10-31 13:58 246 查看
算法(algorithm)是为求解一个问题需要遵循的,被清楚地指定的简单指令的集合。

一旦某种算法给定被确定是正确的,那么重要的一步就是确定该算法将需要多少诸如时间或空间等资源量的问题

四个定义

定义1:如果存在正常数c和n0使得当N>=n0时T(N)=<cf(N),则记为T(N)= O(f(N))

定义2:如果存在正常数c和n0使得当N>=n0时T(N)>=cg(N),则记为

定义3:T(N)=(h(N))当且仅当T(N)=O(h(N))和T(N)=(h(N))

定义4:如果对所有的常数c存在n0使得当N>n0时T(N)<cp(N),则记为T(N)=o(P(N))

相对增长率(relative rates of growth)

如果我们用传统的不等式来比较增长率,

第一个定义就是说T(N)的增长率小于等于(=<)f(N)的增长率,

第二个定义T(N)=(g(N))就是说T(N)的增长率大于等于g(N)的增长率

第三个定义T(N)=(h(N))是说T(N)的增长率等于h(N)的增长率。

第四个定义T(N)=o(P(N))是说T(N)的增长率小于P(N)的增长率,它不同于O,因为O包含增长率相等这种可能性。

在此中注意上界(upper bound)和下界(lower bound)的定义

法则1:如果T1(N)=O(f(N)),T2(N)=O(g(N)),那么

T1(N)+T2(N)= O(f(N)+g(N)),T1(N)*T2(N)=O(f(N)*g(N))

法则2

法则3:对于任意常数k,。它告诉我们对数的增长的非常缓慢

对大部分常见的函数进行分析,增长率越来越快



需要注意的几点:

1 将常数或低阶放进大O是非常不好的习惯,不要说T(N)=O(2N*N)或T(N)=O(N*N+N)在这两种情形下,正确的形式是T(N)=O(N*N),这就是说,低阶项一般可以被忽略,而常数也可以丢弃掉。

2 我们总能够通过计算极限来去确定两个函数f(N)和g(N)的相对增长率,必要的时候可以使用洛必达法则。

模型 

为了在形式的框架中分析算法,我们需要一个计算模型,我们的模型基本上式一台标准的计算机

模型机做任意一件简单的工作都恰好花费一个时间单位

要分析的问题:运行的时间

平均情形性能常常反应典型的结果,而最坏情形的性能则代表对任何可能的输入的性能上的一种保证。

一般来说,若无特别说明,则所需要的量就是最坏情况的运行时间。

如果只是小量输入的情形,那么花费大量的努力去设计聪明的算法恐怕就太不值得了

在一些经典的算法中,数据的读取一般式一个瓶颈,

只要有可能,使得算法足够高效而不至于成为问题的瓶颈是非常重要的。

运行时间计算

为了简化分析,我们将采用如下的约定,不存在特定的时间单位,因此,我们抛弃那些常数系数。我们还将抛弃低阶项,因此所要做的就是计算大O运行时间。由于大O是一个上界,因此我们必须仔细,绝不要低估程序的运行时间,实际上,分析的结果是程序在一定时间范围内终止运行的保证,程序可以提前结束,但决不能拖后。

一般法则:

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

法则2:嵌套循环,从里向外分析这种循环,在一组嵌套循环内部的一条语句总得运行时间为该语句的运行时间乘以该组所有循环的大小得乘积。

Eg: for(int i =0; i < n; i++)

       For(int j = 0; j < n; j++)

             K++

该程序为O(N*N)

法则3:顺序语句 将各个语句的运行时间求和即可

Eg

For(int i = 0; i < n; i++)

      a [i] = 0;

For(i = 0; i < n; i++)

   For(int j = 0; j < n; j++)

              a [i] = a[i-1]+a[i-2];

上段位O(N),下段位O(N*N),所以总量为O(N*N);

法则4:If/Else语句,对于程序片段

If(condition)

   S1

Else

   S2

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

分析的基本策略是从内部(或最深层部分)向外展开工作。

格言: 计算任何事情不要超过一次。

最大子程序和问题的解

算法1

int maxSubsum1(const vector<int>& a)

{

  int maxSum = 0;

  

  for(int i = 0; i < a.size(); i++)

      for(int j = i; j < a.size(); j++)

      {

      int thisSum = 0;

  for(int k = i; k <= j; k++)

     thisSum += a[k];

 

  if(thisSum > maxSum)

      maxSum = thisSum;

  }

    return maxSum;

}

分析算法一可以得到O(N*N*N)

算法二

int maxSubsum1(const vector<int>& a)

{

  int maxSum = 0;

  

  for(int i = 0; i < a.size(); i++)

      for(int j = i; j < a.size(); j++)

      {

      int thisSum = 0;

  thisSum += a[j];

  

  if(thisSum > maxSum)

     maxSum = thisSum;

  }

    return maxSum;

}

T(N)=O(N*N)

算法三

算法三采用一种递归和相对复杂的解法(NlogN),该方法采用分治(divide-and-conquer)策略。其想法是把问题分成两个大致相等的子问题,然后递归地对他们求解,这是“分”的部分,“治”阶段将两个子问题的解合并一起并可能再做少量的附加工作。最后得到整个问题的解。

int maxSumRect(const vector<int>& a, int left, int right)

{

     if(left == right)

    if(a[left] > 0)

   return a[left];

else

   return 0;

int center = (left + right) / 2;

int maxLeftSum = maxSumRect(a, left, center);

int maxRightSum = maxSumRect(a, center+1, right);

int maxLeftBorderSum = 0, leftBorderSum = 0;

for(int i = center; i >= left; i--)

{

   leftBorderSum += a[i];

   if(leftBorderSum > maxLeftBorderSum)

        maxLeftBorderSum = leftBorderSum;

}

int maxRightBorderSum = 0, rightBorderSum = 0;

for(int j = center + 1; j <= right; j++)

{

    rightBorderSum += a[j];

if(rightBorderSum > maxRightBorderSum)

     maxRightBorderSum = rightBorderSum;

}

return max3(maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum);

}

有程序分析可以得到:T(N)=O(NlogN)

算法四

int maxSubSum4(const vector<int>& a)

{

    int maxSum = 0,thisSum = 0;

for(int j = 0; j < a.size(); j++)

{

     thisSum += a[j];

 if(thisSum > maxSum)

    maxSum = thisSum;

 else if(thisSum < 0)

    thisSum = 0;

}

return maxSum;

}

分析可以得到T(N)=O(N)

运行时间中得对数

对数最常出现的规律可概括为下列一般的法则

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

如果使用常量时间只是把问题减少一个常数的数量(例如将问题减少1),那么这种算法就是O(N)

Example1:二分搜索(binary search)

template <typename Comparable>

int binarySearch(const vector<Comparable>& a, const Comparable& x)

{

    int low = 0,high = a.size - 1;

while(low <=high)

{

     int mid = (low + high)/2;

 

 if(a[mid] < x)

    low = mid + 1;

 else if(a[mid] > x)

    high = mid - 1;

else 

    return mid;

}

return -1;

}

Example 2  欧几里得算法 

计算最大公因数的欧几里得算法。

long gcb(long m, long n)

{

    while(n != 0)

{

    long rem = m%n;

m = n;

n = rem;

}

return m;

}

3 幂运算

long pow(long x, int n)

{

     if( n == 0)

 return 1;

 if( n== 1)

 return x;

 if(isEven(n))

    return pow(x*x,n/2);

 else

     return pow(x*x, n/2)*x;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息