(一)数据结构与算法的区别

  • 数据结构:是计算机存储,组织数据的方式。
    指相互之间存在一种或多种特定关系的数据元素的集合。
  • 算法:算法就是一系列的计算步骤,用来将输入数据转化成输出结果。
  • 数据结构是数据元素的集合;算法是一系列的计算步骤。

(二)算法的效率

1. 时间效率(时间复杂度):衡量算法运行的速度(快慢)。

2. 空间效率(空间复杂度):衡量算法运行所需要的额外空间。

(三)时间复杂度

  • 时间复杂度:衡量算法快慢的标准刻度。

  • 虽然是时间复杂度,但是无法直接根据时间来判断算法的快慢,因为根据时间判断会有各种因素的干扰。

  • 所需前提:在cpu单位时间运行指令数恒定的情况下,
    (算法运行的基本指令个数)
    求算法运行指令数和数据规模n的关系 F(n)。

  • 时间复杂度不是绝对意义上的快慢。
    随着数据规模的变化,运行时间的变化趋势。

  • 大O渐进表示法:
    (1)只保留最高次项;
    (2)最高次项的系数化为1.

  • 算法的时间复杂度分为三种情况:

三种情况 说明
最好情况 任意输入规模的最小运行次数(下界)
最坏情况 任意输入规模的最大运行次数(上界)
平均情况 任意输入规模的期望运行次数

注:一般情况下关注的是算法的最坏情况。根据最坏情况计算时间复杂度。

  • 常见的几种时间复杂度
    (1) O(1)
    (2)O(log(n))
    (3)O(n)
    (4)O(n*log(n))
    (5)O(n^2)
    时间复杂度的大小由上到下依次为由小到大。

举例(1):


void Func1(int N){
  int count = 0;
  
  for (int i = 0; i < N ; i++) {
  
    for (int j = 0; j < N ; j++) {
      count++;
    }
  }
  
for (int k = 0; k < 2 * N ; k++) {
  count++;
}

int M = 10;
while ((M--) > 0) {
  count++;
  }
System.out.println(count);
}

分析程序可知:
该程序的基本操作次数为F(N) = N * N+2 * N+10

  • 根据大O渐进表示法:
    (1)只保留最高次项得:F(N) = N * N=N^2
    (2)使最高项次数为1:F(N) = N ^2
    所以该算法的时间复杂度为O(n^2).

举例(2)

void Func2(int N) {
  int count = 0;
  for (int k = 0; k < 2 * N ; k++) {
    count++;
  }
  int M = 10;
  while ((M--) > 0) {
    count++;
  }
  System.out.println(count);
}

分析可得,基本操作次数:F(N) = 2*N +10

  • 根据大O渐进表示法
    (1)取最高项:F(N) = 2*N
    (2)将最高项的系数化为1:F(N) = N
    即该算法时间复杂度为O(n)

举例3:

void Func3(int N, int M) {
  int count = 0;
  for (int k = 0; k < M; k++) {
    count++;
  }
  for (int k = 0; k < N ; k++) {
    count++;
  }
  System.out.println(count);
}

基本操作次数:F(M+N) = M+N
此时有两个可变操作数,系数都为1;
所以时间复杂度为:O(n+m)

举例4:

void Func4(int N) {
  int count = 0;
  for (int k = 0; k < 100; k++) {
    count++;
  }
  System.out.println(count);
}

分析可知基本操作次数为:F(N)=100
没有可变的操作数,直接将系数化为1即可。
所以时间复杂度为O(1)

举例5:

// 计算 String 类的 indexOf 的时间复杂度?
int String.indexOf(int ch);

分析可得:该算法在最好情况下执行一次,在最坏情况下执行n次
所以取最坏情况。
时间复杂度为:O(n)

举例6:

// 计算冒泡排序的时间复杂度
void bubbleSort(int[] array){
     for(int i = 0 ; i < array.length-1; i++){
//每次冒泡之前,假设数组已经存在
      boolean sorted = true;
        for(int j = 0 ; j <= arrary.length-2-i; j++){
           if(array[j] > array[j+1]{
             int t = array[j];
             array[j] = array[j+1];
             array[j+1] = t;
             sorted = false;
         }
       }
       if(sorted == true){
         break;
       }
     }
   }
}

分析得:在最好情况下,需要n次基本操作;在最坏情况下,需要 n*(n-1)次操作。取最坏情况,则基本操作数为 F(n) = n*(n-1)/2=1/2n^2-1/2n.
(1)取最高次项: F(n) =1/2 n^2
(2)最高次项系数化为1:F(n) = n^2;
所以该算法得时间复杂度为O( n^2)

举例7:

// 计算二分查找的时间复杂度?
int BinarySearch(int[] array, int value) {
  int begin = 0;
  int end = array.length - 1;
  while (begin <= end) {
    int mid = begin + ((end-begin) / 2);
    if (array[mid] < value)
      begin = mid + 1;
    else if (array[mid] > value)
      end = mid - 1;
      else
        return mid;
  }
return -1;
}

基本操作执行最好1次,最坏O(logN)次,时间复杂度为 O(logN)

举例9:

// 计算阶乘递归Factorial的时间复杂度?
long Factorial(int N) {
return N < 2 ? N : Factorial(N-1) * N;
}

通过计算分析发现基本操作递归了N次,时间复杂度为O(N)

举例10:

// 计算斐波那契递归Fibonacci的时间复杂度?
long Fibonacci(int N) {
  return N < 2 ? N : Fibonacci(N-1)+Fibonacci(N-2);
}

例如n= 5时:
数据结构(1)——数据结构的概念,时间复杂度,空间复杂度
通过分析发现基本操作递归了2 ^N次,
时间复杂度为O(2^N)。

(四)空间复杂度

大O渐进表示法:
数据规模是n得情况下:
(1)额外使用的空间大小(不考虑输入/输出中用到的空间)
(2)常见形式:
i:int[] array = new array[];
ii: 递归函数
举例1:

// 计算冒泡排序的空间复杂度
void bubbleSort(int[] array){
     for(int i = 0 ; i < array.length-1; i++){
      boolean sorted = true;
        for(int j = 0 ; j <= arrary.length-2-i; j++){
           if(array[j] > array[j+1]{
             int t = array[j];
             array[j] = array[j+1];
             array[j+1] = t;
             sorted = false;
         }
       }
       if(sorted == true){
         break;
       }
     }
   }
}

使用了常数个额外空间,所以空间复杂度为 O(1)

举例2:

// 计算Fibonacci的空间复杂度?
long[] Fibonacci(int n)
{
  long[] fibArray = new long[n + 1];
  fibArray[0] = 0;
  fibArray[1] = 1;
  for (int i = 2; i <= n ; i++) {
    fibArray[i] = fibArray[i - 1] + fibArray [i - 2];
  }
  return fibArray;
}

动态开辟了N个空间,空间复杂度为 O(N)。

举例3:

// 计算阶乘递归Factorial的空间复杂度?
long Factorial(int N) {
return N < 2 ? N : Factorial(N-1)*N;
}

动态开辟了N个空间,空间复杂度为 O(N)。

相关文章: