数据结构与算法(一):时间复杂度与空间复杂度

写在前面:

该系列博客,是本人系统学习数据结构以及算法的笔记类博客,用于记录一些知识点以及自己所实现的代码,以作备忘,欢迎批评交流。

下面开始正文,介绍一下数据结构及算法的入门定义。

目录

1. 什么是数据结构、算法

2. 复杂度分析

2.1 时间复杂度

2.2. 空间复杂度

3. 最好、最坏、平均、均摊时间复杂度


1. 什么是数据结构、算法

简而言之,数据结构就是数据的存储结构,算法就是操作这些数据的方法

两者都是前人为了更高效、更节省空间地进行数据处理而创造的,所聚焦的是“快”和“省”两个字。

2. 复杂度分析

既然已经知道,数据结构和算法是为了快且省地进行数据操作,那么就需要一个指标来评价你的算法到底有多快、多省。

复杂度分析即是为此而生,包括时间复杂度分析、空间复杂度分析两种。

主要用到大O分析法:T(n) = O(f(n)),表示的是复杂度的量级,也即不是具体的时间、内存消耗,而是其消耗与数据规模n相关的量级。

常用的复杂度有:常量阶:O(1) 对数阶:O(log_n) 线性阶:O(n) 线性对数阶:O(nlogn) k次方阶: 指数阶: 阶乘阶:

  • 常量阶:O(1)
  • 对数阶:O(logn)
  • 线性阶:O(n)
  • 线性对数阶:O(nlogn)
  • k次方阶:O(n^k)
  • 指数阶:O(2^n)
  • 阶乘阶:O(n!)

2.1 时间复杂度

分析时间复杂度时,需要假设每一个单元操作是一个单位时间,然后只需要统计整个程序的操作次数再取最大量级即可。

下面列举几种复杂度的程序,即可一目了然:

// 程序A:
int a=1;
int b=2;

// 程序B:
int i=1;
while (i <= n)  {
   i = i * 2;
 }

// 程序C:
int i=1;
int sum=0;
while (i <= n)  {
   sum += i;
}

对于程序A,执行了两句话,其量级是常量,因此时间复杂度为O(1)

对于程序B,执行了log_2n次,因此其时间复杂度为O(logn)

对于程序C,则执行了n次,因此其时间复杂度为O(n)

其他的时间复杂度同理,只需要记住:我们所求的只是最大量级。

此外,对于有两个或者多个数据规模的情况,则需要对每个规模单独计算时间复杂度,然后相加,如:


int cal(int m, int n) {
  int sum_1 = 0;
  int i = 1;
  for (; i < m; ++i) {
    sum_1 = sum_1 + i;
  }

  int sum_2 = 0;
  int j = 1;
  for (; j < n; ++j) {
    sum_2 = sum_2 + j;
  }

  return sum_1 + sum_2;
}

上述代码的时间复杂度为:O(m+n).

对于嵌套代码的复杂度,等于嵌套内外代码复杂度的乘积。这个不难理解,想象一个双重for循环:


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

其时间复杂度为:O(n^2).

2.2. 空间复杂度

空间复杂度主要从程序运行过程所申请内存大小来判断,表示算法的存储空间与数据规模之间的增长关系。

以上述程序A为例,其空间复杂度为O(1)。再举个例子:

int n = 1000;
int[] a = new int[n];

其空间复杂度则为O(n),因为申请了n个int型内存空间。

3. 最好、最坏、平均、均摊时间复杂度

空间复杂度不必多讲,对于时间复杂度,有时候需要考虑不同情况下复杂度的不同,主要分为:最好、最坏、平均、均摊几种。

最好情况时间复杂度:在最理想的情况下,执行这段代码的时间复杂度;

最坏情况时间复杂度:在最糟糕的情况下,执行这段代码的时间复杂度;

平均时间复杂度:各种情况下的平均情况;

以代码为例:


// 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;
}

可简单得出:最好情况下,时间复杂度为O(1),最坏情况下为O(n)

而平均时间复杂度呢,上述代码中,是用于查找数组中指定元素的程序,所以分两种情况:x在数组中、x不在数组中,假设每种情况的概率为1/2,对于在数组中的情况,x在数组各个位置的概率又为1/(2n),因此把所有情况加起来即为:

1*\tfrac{1}{2n} +2*\tfrac{1}{2n}+....+n*\tfrac{1}{2n} + n*\tfrac{1}{2} = \tfrac{3n+1}{4}

因此,平均时间复杂度也为O(n)

此外,还有一个概念——均摊时间复杂度。这个概念是用于描述好情况、坏情况有规律重复的时候,将坏情况复杂度直接均摊给所有简单情况,从而计算时间复杂度的方法。

比如:对于一组规模为n的数据,前n-1中情况,都是复杂度为O(1),第n种情况复杂度为O(n),则利用均摊计算方法,其均摊时间复杂度为O((n+1)/n) = O(1).

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章