数据结构与算法之——算法复杂度分析

什么是数据结构与算法?
数据结构就是数据的存储方式,比如数组就是把数据存在一段连续的内存上,而链表则是通过指针的关联将数据存在任意可用的内存上;栈是先进后出,队列是先进先出。

而算法则是对这些数据的操作方法,比如数据的插入、查找、删除、排序等。

二者相辅相成,互为一体,数据结构为算法服务,而算法要在指定数据结构上进行操作。

1. 关于复杂度分析

学习数据结构和算法的目的是为了在实际应用的时候更加优化地利用内存,提高程序运行效率,而复杂度分析则是给我们提供一个衡量代码质量好坏的标准。

如果我们在不运行程序的情况下就可以定性知道代码的内存占用和时间消耗,这将会给我们提供一个当前程序的总体评估和未来的改进方向。

直接运行程序就可以知道算法的执行时间和占用内存,但这个过程往往会受到运行环境和数据规模的影响,因此,我们需要一个不用进行具体测试就可以粗略估计算法执行效率的方法,这就是复杂度分析。

2. 时间复杂度

2.1 大 O 复杂度表示法

int cal(int n) 
{
   int sum = 0;
   int i = 1;
   for (; i <= n; ++i) 
   {
      sum = sum + i;
   }
   return sum;
 }

我们假设每行代码的运行时间为 t,则第一二行代码需要时间为 \(2 \times t\),第三四行代码需要时间为 \(2n \times t\),总时间为 \((2n+2)\times t\),代码运行总时间与 \(n\)成正比。
用大 \(O\) 法可表示为\(O(2n+2)\),这并不代表代码的实际执行时间,只是表征代码执行时间随数据规模的变化趋势
当 \(n\) 足够大时,低阶、常量和系数就可以忽略不计,直接表示为 \(O(n)\)。

2.2 常用分析方法

  • 循环最多代码,重点关注
  • 串行代码,复杂度相加
  • 嵌套代码,复杂度相乘

2.3 几种常见复杂度

  • 多项式量级
  • 常量阶 
  • 对数阶
  • 线性阶
  • 线性对数阶
  • 乘方阶
  • 非多项式量级(Non-Deterministic Polynomial)
  • 指数阶
  • 阶乘阶
  • 非多项式量级的算法的执行时间会随着数据规模的扩大急剧增加,是非常低效的算法。

时间复杂度如图所示:

attachments-2019-12-DiKWjHYW5def519e11a3a.png

2.4 进阶情况

  • 最好情况时间复杂度(Best Case Time Complexity)
  • 最坏情况时间复杂度(Worst Case Time Complexity)
  • 平均情况时间复杂度(Average Case Time Complexity)

以查找为例,看如下代码

// n 表示数组 array 的长度
int find(int[] array, int n, int x) 
{
  int i = 0;
  int pos = -1;
  for (; i < n; ++i) 
  {
    if (array[i] == x) 
    {
       pos = i;
       break;
    }
  }
  return pos;
}

最好情况时间复杂度就是在程序最理想的状态下,数组第一个元素就是我们要查找的元素,只需要查找一次;而最坏情况时间复杂度就是在程序最糟糕的状态下,数组最后一个元素才是我们要查找的元素,需要查找完整个数组;

事实上,我们要查找的元素可能存在数组中的任何一个位置,甚至可能不存在于数组中,因此,考虑所有情况出现的概率,求出各种情况下时间复杂度的平均值,也就是平均情况时间复杂度。

  • 均摊情况时间复杂度(Amortized Case Time Complexity)
// array 表示一个长度为 n 的数组
// 代码中的 array.length 就等于 n
int[] array = new int[n];
int count = 0;
 
void insert(int val) 
{
    if (count == array.length) 
    {
       int sum = 0;
       for (int i = 0; i < array.length; ++i) 
       {
          sum = sum + array[i];
       }
       array[0] = sum;
       count = 1;
    }

    array[count] = val;
    ++count;
 }

这段代码的功能是向数组中插入一个元素,当数组未满时,直接插入,时间复杂度为\(O(1)\) ;当数组满时,先计算数组所有元素的和,再插入元素,时间复杂度为\(O(n)\) 。

  • 分享于 2019-12-09 09:02
  • 阅读 ( 99 )

[版权声明] :本文系网友分享,仅以非商业性的交流和科研为目的,并不代表本网赞同其观点和对其真实性负责。如涉及作品内容、版权和其它问题,请在30日内与本网( friends@stuch.cn )联系!我们将协调给予处理。阅读原文(请登录)..

0 条评论

请先 登录 后评论
猜猜我是谁
甜也 -研究生

9
提问
8
回答
5
文章