算法初步

0. 从1加到100?

先写一个从1加到100的程序,如何写呢?
记刚学程序时的写法:

 int i, sum = 0, n = 100;

 for(i = 0; i < 100; i++)
 {    
     sum += i;
 }
 printf("%d", sum);

大神高斯小学时的解法用程序实现:

 int sum = 0, n = 100;
 sum = (1 + n) * n / 2;
 printf("%d", sum);

我们看到的这两种解决从1加到100的解决方法,就是算法。

1. 算法定义

算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或者多个操作。

2. 算法的特性

  • 输入输出:算法具有零个或者多个输入。对于多数算法来说,输入参数是必要的,但是对于打印“hello world!”这样的代码,就不需要参数。算法至少有一个或多个输出。倘若一个算法没有输出,那我们要它何用?当然输出方式可以是打印输出或者返回一个或多个值等。
  • 有穷性:指算法在执行有限步骤后,自动结束而不是无限循环,并且每一个步骤在可以接受的时间内执行完成。这里的有穷也是有边界的,并不是说执行一个算法需要二三十年也是能完成的,那样黄瓜菜都凉了,意义不大了。
  • 确定性:算法的每一个步骤都有确定意义的,不会出现二义性。
  • 可行性:算法的每一步骤都必须是可行的,也就是说,每一个步骤都能执行有限次数后完成。

3. 算法效率的度量方法

3.1 事后统计法

通过设计好的测试程序和数据,利用计算机计时器对不同的算法编制的程序的运行时间进行比较,从而确定算法的效率高低。

3.2 事前分析估算法

在算法编制前,根据统计方法对算法进行估算。

算法效率的度量也就是对算法运行时间的度量,而一个程序的运行时间,主要依赖于算法的好坏和输入规模。
输入规模即输入量的多少。
看栗子:

第一种求和算法:

 int i, sum = 0, n = 100;    //执行1次

 for(i = 0; i < 100; i++)    //执行年n+1次
 {    
     sum += i;                //执行n次
 }
 printf("%d", sum);            //执行1次

第二种求和算法:

 int sum = 0, n = 100;    //执行1次
 sum = (1 + n) * n / 2;    //执行1次
 printf("%d", sum);        //执行1次

显然,第一种算法执行了2n+3次,第二种算法执行了3次,两种算法的好坏显而易见。

4. 函数的渐进增长

举个例子:
相同的输入规模n,算法A做2n+3次操作,算法B做3n+1次操作。两个算法谁更快呢?

显然,答案不确定,当n=1时,算法B快些;当n=2时,两者相等;当n>2时,算法A快。

此时给出这样的定义,输入规模n在没有限制的情况下,只要超过一个数值N,这个函数就总是大于另一个函数,我们就称函数是渐进增长的。

函数的渐进增长:给定两个函数f(n)和g(n),如果存在一个整数N,
使得对于所有的n>N,f(n)总是比g(n)大,那么,我们说f(n)的增长渐进快于g(n)。

根据函数的渐进增长性我们可以得出:某个算法,随着n的增大,它会越来越优于另一个算法,或者越来越差于另一个算法。

5. 算法的时间复杂度

5.1算法时间复杂度定义

在进行算法分析时,语句总的执行次数T(n)是关于输入规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度,也就是算法的时间度量,记作:T(n) = O(f(n))。它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐进时间复杂度,简称时间复杂度。其中f(n)是问题规模n的某个函数。

5.2 大O阶的推导

大O阶的推导:
1.用常数1取代运行时间中的所有加法常数。
2.在修改后的运行次数函数中,只保留最高阶项。
3.如果最高阶项存在且不是1,则去除与这个项相乘的常数。
得到的结果就是大O阶。

5.3 常数阶O(1)

 int sum = 0, n = 100;    //执行1次
 sum = (1 + n) * n / 2;    //执行1次
 printf("%d", sum);        //执行1次

5.4 线性阶O(n)

 int i;
 for(i = 0; i < n; i++)
 {
 //时间复杂度为O(1)的程序步骤序列
 }

5.5 对数阶O(logn)

 int count = 1;
 while(count < n)
 {
     count = count * 2;
 //时间复杂度为O(1)的程序步骤序列
 }

5.6 平方阶O(n²)

 int i, j;
 for(i = 0; i < n; i++)
 {
     for(j = 0; j < n; j++)
     {
     //时间复杂度为O(1)的程序步骤序列
     }
 }

计算如下程序的时间复杂度:

 int i, j;
 for(i = 0; i < n; i++)
 {
     for(j = i; j < n; j++)
     {
     //时间复杂度为O(1)的程序步骤序列
     }
 }

当i=1时,内循环执行了n-1次;当i=n-1时,内循环执行了1次,我们可以推算出总的执行次数为:
n+(n-1)+(n-2)+(n-3)+……+1=n(n+1)/2=n²/2+n/2
用大O推导法,可得这段代码的时间复杂度为O(n²)

5.7 常见时间复杂度

 O(1)<O(logn)<O(n)<O(nlogn)<O(n²)<O(n³)<O(2ⁿ)<O(n!)

猜你喜欢

转载自www.cnblogs.com/yaofan/p/9760159.html