数据结构与算法分析笔记-第二章

第二章 算法分析


  2.1数学基础


    讲的是函数的增长,显然比《算法导论》简洁得多。算法导论用了一章来讲的……我已经翻过算法导论了,所以只是复习一下。
      以n^2为例,Ο定义的是最多也才n^2,Ω则是最少那么多。
      如果是Θ则定义了上界与下界,是准确加在中间的。
      ο则是表示它最多n^2但是达不到n^2,比如一个Θ(n),它肯定是不会超过Ο(n^2)的,也达不到Ο(n^2)。
    这种微积分的东西还是要自己看定义才能更清楚、准确地理解。最后讲了几个计算用的结论。

  2.2模型


    就是RAM模型啦……
 

  2.3

提出了最大子序列和问题并用图像和表格告诉你:降低算法的复杂度是很重要的

  2.4最大子序列


  2.4.1与2.4.2介绍了分析算法复杂度的几个法则,并详细分析了上面说的fib计算的效率达到了指数级。还提到了一句格言:计算任何事情不要超过一次。
  2.4.3提出了最大子序列问题的4个解,分别是n^3,n^2,nlogn与n的,通过实例讲解上面提到的分析方法。
      n^3的方法是枚举起始点与终点,再用一个循环统计和
      n^2的方法是枚举起点与子序列的长度
      nlogn的方法很巧妙,书中说:要是没有那个线性的算法,那么这个算法就是体现递归威力的极好的范例了。采用分治的思想,把数据分成两部分,和最大的子序列要么在左边,要么在右边,要么横跨两边,所以递归计算两边,再从中间开始往两边加计算横跨两边的时候的最大子序列和,比较一下返回最大的就行了。
      我模仿书中的代码打了一遍,帕斯卡命名法真的好麻烦,要时常按shift打大写字母,规范的变量命名确实让程序好看多了,以前自己乱打的a,b,c命名变量的程序,时间一长自己都看不懂了。看规范的代码简直是一种享受啊。
      n的线性算法更NB,常数空间,线性时间就能算出来结果,而且是online algorithm,非常人能够独立想出来的。这个算法也是经典的DP题。思路还是看代码吧。
      我把分治的算法与动态规划的解打了一遍,更加理解Vim了,正是恨不得有个shift键的脚踏板。
uses math;
var
  a:array[1..10000] of longint;
  i,n:longint;
{============================algorithm1==============================}
function MaxSum(left,right:longint):longint;
var
  i,mid:longint;
  MaxLeftSum,MaxRightSum,LeftMax,RightMax,LeftSum,RightSum:longint;

begin
  if left=right then
    if a[left]>0 then exit(a[left])
    else exit(0);

  mid:=(left+right) div 2;
  MaxLeftSum:=MaxSum(left,mid);
  MaxRightSum:=MaxSum(mid+1,right);

  LeftMax:=0; RightMax:=0;
  LeftSum:=0; RightSum:=0;
  for i:=mid downto left do
  begin
    LeftSum:=LeftSum+a[i];
    if LeftSum>LeftMax then LeftMax:=LeftSum;
  end;
  for i:=mid+1 to right do
  begin
    RightSum:=RightSum+a[i];
    if RightSum>RightMax then RightMax:=RightSum;
  end;

  exit( max(LeftMax+RightMax , max( MaxLeftSum , MaxRightSum) ) );
end;
{============================algorithm2==============================}
function MaxSum_dp:longint;
var
  i,ThisSum,MaxSum:longint;
begin
  ThisSum:=0; MaxSum:=0;
  for i:=1 to n do
  begin
    inc(ThisSum,a[i]);
    if ThisSum>MaxSum then MaxSum:=ThisSum
    else if ThisSum<0 then ThisSum:=0;
  end;
  exit(MaxSum);
end;
{=============================main===================================}
begin
  readln(n);
  for i:=1 to n do read(a[i]);
  writeln(MaxSum(1,n));
  writeln(MaxSum_dp);
end.

  2.4.4讲的是几个复杂度是logn的算法:二分查找,辗转相除法与快速幂,都是非常经典的并且很常见的算法啊,还是打一下吧。
 

欧几里德辗转相除法:


var

  m,n:longint;

function gcd(m,n:longint):longint;
var
  x:longint;
begin
  while (n>0) do
  begin
    x:=m mod n;
    m:=n;
    n:=x;
  end;
  exit(m);
end;

begin
  readln(m,n);
  writeln(gcd(m,n));
end.

   

二分查找(书中的翻译是对分查找):


var
  a:array[1..10000] of longint;
  i,x,n:longint;
function find(x:longint):longint;
var
  low,high,mid:longint;
begin
  low:=1; high:=n;
var
  a:array[1..10000] of longint;
  i,x,n:longint;
function find(x:longint):longint;
var
  low,high,mid:longint;
begin
  low:=1; high:=n;
  while low<=high do
  begin
    mid:=(low+high) div 2;
    if a[mid]<x then low:=mid+1
    else if a[mid]>x then high:=mid -1
         else exit(mid);
  end;
end;

begin
  readln(n);
  for i:=1 to n do read(a[i]);
  read(x);
  writeln(find(x));
end.
  while low<=high do
  begin
    mid:=(low+high) div 2;
    if a[mid]<x then low:=mid+1
    else if a[mid]>x then high:=mid -1
         else exit(mid);
  end;
end;

begin
  readln(n);
  for i:=1 to n do read(a[i]);
  read(x);
  writeln(find(x));
end.
    快速幂:
var
  n,x:longint;

function pow(x,n:longint):longint;
begin
  if n=0 then exit(1);
  if n=1 then exit(x);
  if odd(n) then exit(pow(x*x,n div 2)*x)
  else exit(pow(x*x,n div 2));
end;

begin
  readln(x,n);
  writeln(pow(x,n));
end.
    打快速幂的时候竟然又把变量打错了,想起了好几个月才通过的麦森数……
    n=1其实不用另外判断的,因为下面当n为奇数的时候的处理是一样的。
    要注意不要进入无限递归与重复计算

  练习


  2.6我做了,是五个主要有for循环与if语句构成的程序段判断运行时间。
  2.7挺有意思,生成前N个自然数的随即置换。题目给出了三种方法:
    a.产生随机数直到不同于已经填入的数组元素,效率是n^2logn
    b.建一个used数组标记已经用过的元素,nlogn
    c.for i:=1 to n do swap(a[i],radrom(0,i)),liner
      答案提到把radrom(0,i)换成(0,n)就不行了,若n=3,用这种方法有27种可能,而合理的方案有6种,27 mod 6<>0
  2.10有必要提一下,高中数学教科书上说的是海伦-秦九昭算法,CLRS与本书都叫做Horner的计算多项式相加的算法,即计算sum(Ai*X^i)的算法;
    for i:=n downto 1 do poly:=poly*x+Ai
    能有效降低计算的次数,答案说运行的效率是liner的
  2.11寻找已排序的A数组中是否有Ai=i,答案是说用变种的二分查找能得到logn的算法,有机会要仔细考虑一下。
  2.12题目挺有意思,可是没答案,有时间好好想吧……
  2.19查找主要元素不失为一道不错的练习题,有时间要好好研究一下

 

总结

这一章内容比较多,应该掌握渐进符号(虽然不搞研究的话没什么用),最大子序列和问题和算法分析的基础,还有就是那三个logn的算法,特别常见。

posted on 2011-01-21 09:12  oa414  阅读(363)  评论(0编辑  收藏  举报

导航