《数据结构与算法分析》第二章算法分析笔记、时间复杂度、大O表示法

160 阅读2分钟

本文已参与[新人创作礼]活动,一起开启掘金创作之路

大O表示法

O(n^2 / n^3) ...

O(n*logn)

O(n)

线性增长, 斜率为n
  • 示例,计算i = 0, 到 i = n 时,i^3 积分

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 
      4 size_t sum(int n)
      5 {
      6     size_t res = 0;
      7 
      8     for (int i = 1; i <= n; i++)
      9         res += i * i * i;
     10 
     11     return res;
     12 }
     13 
     14 int main(int argc, char** argv)
     15 {
     16     if (argv[1] == 0)
     17         argv[1] = "1";
     18 
     19     int n = atoi(argv[1]);
     20 
     21     printf("%ld\n", sum(n));
     22 
     23     return 0;
     24 }
    
    
    gcc -std=gnu11 -g -O0 cal.c -o cal.out
    

第9行有两次乘积,一次加法,一次赋值,5次,第8行有一次加法,一次比较,第一行,第六行也有时间开销,大致为7n+2,为线性增加

  • 推理
    • 一阶循环
      • O(n)
    • 二阶循环
      • O(n^2)
      for (int i = 0; i < n; i++)
          for (int j = 0; j < n; j++)
              res += j;
      
  • 递归的时间复杂度分析
    • 斐波那契计算
      size_t fab(int n)
      {
          if (n <= 1)
              return 1;
          else
              return fab(n - 1);
      }
      
      • 时间复杂度为O(n),随着n的递增,递归迭代的次数同步递增,斜率为n
    • 另一个递归
      size_t fun(int n)
      {
          if (n <= 1)
              return 1;
          else
              return fun(n - 1) + fun(n - 2);
      }
      
      • 随着n的增加,每次迭代多了两次,迭代次数为 1, 3, 7, 15...,复杂度为O(n^2)
      • 该算法的问题在于,有重复的算法,如计算fun(n - 1) + fun(n - 2)时,fun(n - 1)会再次计算一遍fun(n - 2)

最大子序列的4种算法

参考 CSDN 最大子序列和的四种算法,代码拷贝来自此处

算法思想,已知一个有符号位整数数组,a[n],求在整个数组空间中,哪一段连续空间可以得到最大值

数组内可能有负数,所有整个数组的空间内所有数之和并不一定是最大数,哪一段空间内有可能求和最大都有可能

  • 暴力求解

    public static int maxSubSum1(int[] a) {
    	int maxSum = 0;
    	int sum;
    	for (int i = 0; i < a.length; i++) {
    		for (int j = i; j < a.length; j++) {
    			sum = 0;
    			for (int k = i; k <= j; k++) {
    				sum += a[k];// 计算a[i]到a[j]的和
    			}
    			if (sum > maxSum) {
    				maxSum = sum;
    			}
    		}
    	}
    	return maxSum;
    }
    
    • 时间复杂度 O(n^3)
  • 初次简化

    • 在 i 不变时,j 的每次增加,其实都会用到上次 i 到 j - 1 的结果
    public static int maxSubSum2(int[] a) {
    int maxSum = 0;
    int sum;
    for (int i = 0; i < a.length; i++) {
    	sum = 0;
    	for (int j = i; j < a.length; j++) {
    		sum += a[j];
    		if (sum > maxSum) {
    			maxSum = sum;
    		}
    	}
    }
    return maxSum;
    }
    
    • 时间复杂度 O(n^2)
  • 分支法,最大值一定出现在左侧或右侧

    • 太麻烦,没看
  • 最优起点

    • 算法思想:设a[i]为和最大序列的起点,则如果a[i]是负的,那么它不可能代表最优序列的起点,因为任何包含a[i]作为起点的子序列都可以通过a[i+1]作起点而得到改进。类似的,任何负的子序列也不可能是最优子序列的前缀。
    public static int maxSubSum4(int[] a){
    	int maxSum=0,sum=0;
    	for(int i=0;i<a.length;i++){
    		sum+=a[i];
    		if(sum>maxSum){
    			maxSum=sum;
    		}else if(sum<0){
    			sum=0;
    		}
    	}
    	return maxSum;
    }
    
    • 运行时间:O(N)

最优起点算法的特点

  • 没有递归,不容易跑飞
  • 不需要大容量存储
  • 一次遍历即可
  • 对给定数组的“兼容性”强

O(logn)型

二分法求坐标

一直一个已经排序的数组a[n],求a[i] = X 的坐标i,如果没有返回-1

方法:先假设i处于数组中间,如果是,返回该下表,如果不是,重新设定数组为当前已被分割的数组的左半部分或右半部分

最大公因数 欧几里得算法

算法原理不好讲,可能是数学方便,不懂,不看

幂运算

  • 算法
    size_t pow(int x, int n)
    {
        if (n == 0)
            return 1;
        else if (n == 1)
            return x;
        else if (n % 2 == 1)
            return pow(x * x, n / 2) * x;
        else
            return pow(x * x, n / 2);
    }
    
    • 特点
      • 一次递归只有一个分支
      • 递归操退出方向前进
      • n的收敛速度很快

课后习题

2.6

  1. O(n),n = 1,直线
  2. O(n^2),n^2,2次方
  3. O(n^3),n^3,3次方
  4. O(n^2),n^2,2次方,猜测
  5. 往后不会,猜测