时间复杂度和空间复杂度计算(数据结构C语言实现1)

简介: 时间复杂度和空间复杂度计算(数据结构C语言实现1)

基本概念

1.什么是时间复杂度和空间复杂度?


1.1算法效率算法效率分析分为两种:


第一种是时间效率

第二种是空间效率

时间效率被称为时间复杂度,而空间效率被称作空间复杂度。

时间复杂度主要衡量的是一个算法的运行速度,而空间复杂度主要衡量一个算法所需要的额外空间,在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度。


1.2 时间复杂度的概念时间复杂度的定义:


在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一个算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。但是我们需要每个算法都上机测试吗?是可以都上机测试,但是这很麻烦,所以才有了时间复杂度这个分析方式。一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度。


1.3 空间复杂度的概念:


空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度。空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟实践复杂度类似,也使用大O渐进表示法。


总结


时间复杂度并不是算法运行的时间,而是算法中基本操作(循环等)执行的次数;

因为相同的程序,在不同的机器上运行的时间不同,不同硬件比效率就是耍流氓。

空间复杂度计算的也不是计算程序占用多少个byte空间,而是计算程序变量的个数。


1.4 复杂度计算在算法的意义


出现在一些笔试题目里

像很多算法题,不仅要我们实现算法的功能,还要对复杂度有一定的要求,不然通过不了。也通常出现在一些公司的笔试题中。


工作对算法的优化

当我们进入公司工作时,但老板说优化一下算法时,要知道如何优化,将时间复杂度和空间复杂度控制到最小。


所以时间复杂度和空间复杂度的学习也是十分重要的


常见算法的时间复杂度计算

请计算Func1执行次数?


void Func1(int N)
{    
    int count = 0;   
     for (int i = 0; i < N ; ++ i)   //循环 N次
      {  
        for (int j = 0; j < N ; ++ j)  //循环 N次 
          { 
             ++count;       
          }   
     }    
     for (int k = 0; k < 2 * N ; ++ k)  //循环 2N次
       {
       ++count;  
       }   
      int M = 10;    
      while (M--)    //循环 M次
        {
        ++count;    
        }   
         printf("%d\n", count);
 }

我们已经知道,算法的时间复杂度,就是程序基本操作的执行次数(循环的次数),这便是求复杂度的题目。

我们先来计算这个算法实际循环的次数: N*N+2N+M

计算过程:

一开始两层for循环嵌套次数为N*N,然后一层for循环2N,最后一层while循环执行了M次,M=10,所以执行次数为 N*N+2N+10。

所以这个算法的时间复杂度为N*N+2N+10?


N=10    Func1(10)=10*10+2*10+10=130
N=100   Func1(100)=100*100+2*100+10=10210
N=1000  Func1(1000)=1002010

可以看到当N越来越大时,2N+10对结果的影响并不大,所以我们能否省略?


实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,那么这里我们使用大O的渐进表示法。


大O符号(Big O notation):


是用于描述函数渐进行为的数学符号。


推导大O阶方法:

1、用常数1取代运行时间中的所有加法常数。

2、在修改后的运行次数函数中,只保留最高阶项。

3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。

利用上面的规则得到的结果就是大O阶。

使用大O的渐进Func1的时间复杂度为:O(N^2)

利用第2点,就得到了结果O(N^2)


通过上面我们会发现大O的渐进表示法去掉了那些对结果影响不大的项,简洁明了的表示出了执行次数。

另外有些算法的时间复杂度存在最好、平均和最坏情况:


最坏情况:任意输入规模的最大运行次数(上界)

平均情况:任意输入规模的期望运行次数

最好情况:任意输入规模的最小运行次数(下界)

例如:在一个长度为N数组中搜索一个数据x

最好情况:1次找到

最坏情况:N次找到

平均情况:N/2次找到

在实际中一般情况关注的是算法的最坏运行情况,所以数组中搜索数据时间复杂度为O(N)


2.3常见时间复杂度计算举例

有了上面的学习,我们已经知道了时间复杂度如何计算,接下来我们一起了解一下几种常见的时间复杂度计算。

实例1:


// 计算Func2的时间复杂度?
void Func2(int N)
{    
      int count = 0;  
      for (int k = 0; k < 2 * N ; ++ k)  
        {
        ++count;  
        }   
         int M = 10;    
       while (M--)   
          {
          ++count; 
          }    
          printf("%d\n", count);
  }

实际次数:2N+10

利用大O表示法规则1,保留最高阶数:2N

规则3,省略最高阶数常数:N

所以Func2时间复杂度为:O(N)


实例2


// 计算Func3的时间复杂度?
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; 
     }
     printf("%d",count);
 }

实际次数:N+M

时间复杂度:O(N+M)

如果题目特别强调

1,N远大于M 或者2,N和M相差不大

时间复杂度为O(N)

实例3



// 计算Func4的时间复杂度?
void Func4(int N)
{   
    int count = 0;    
   for (int k = 0; k < 100; ++ k)  
    {
    ++count;  
     }   
     printf("%d\n", count);
 }

实际次数:100

利用规则1,不管N取多少,该算法的执行次数不变,都为10次,常数同一用O(1)

时间复杂度:O(1)

实例4


// 计算strchr的时间复杂度?
const char * strchr ( const char * str, char character )
{ 
   while(*str != '\0')  
     {
        if(*str == character)
           return str;
      ++str;
     }  
 return NULL;
 }

这是一个字符查找的程序,所以情况不同,执行次数不同,而大O表示法,计算的是最坏情况,

时间复杂度:O(N)


实例5


// 计算BubbleSort的时间复杂度?
void BubbleSort(int* a, int n)
{  
  assert(a); 
     for (size_t end = n; end > 0; --end)  //确定躺数   
     {
     int exchange = 0;
          for (size_t i = 1; i < end; ++i)   //一趟移动次数
            {
              if (a[i-1] > a[i])    
                {
                Swap(&a[i-1], &a[i]);//比较
                exchange = 1;
                }      
             }
        if (exchange == 0)
        break;  
      }
 }

这是个冒泡算法,我们要知道该程序的执行过程,才能够计算出该程序的时间复杂度。


第一趟冒泡

image.png

第二趟冒泡,只要到倒数第二个元素,

所以执行次数为


N

N-1

N-2

1


等比数列:(N+1)*N/2

时间复杂度:O(N^2)

实例6


// 计算BinarySearch的时间复杂度?
int BinarySearch(int* a, int n, int x)
{   
    assert(a);    
    int begin = 0; 
    int end = n;  
    while (begin < end)    
    {
      int mid = begin + ((end-begin)>>1);
      if (a[mid] < x)
        begin = mid+1;
        else if (a[mid] > x)
               end = mid;
             else
             return mid; 
    }    
    return -1;
}

二分查找:又叫折半查找,过程如下:

image.png

就如同折纸

第一次 N/2 ,第二次N/2/2,…假设次数为x, 2^x=N ,所以x=log2^N

大O表示法,一般把2省略,

即时间复杂度:O(logN)

实例7


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

递归计算N的阶层,N!=1* 2 * 3.....*N

image.png

可以看到N=4递归了4次

时间复杂度:O(N)


时间复杂度对比

image.png

可以看到O(1)和O(logN)最小,然后是O(N)


可能你会疑惑,为啥O(logN)的时间复杂度这么小?

列如:如果我们用二分查找,在中国14亿人中,假如有序,你想找的某个人,需要多少次呢?


2^10=1024
    2^20=100万
    2^30=10亿

可以看到10亿只需要查找10次,所以14亿也就查找31次。

O(logN)的效率可想而知!


常见空间复杂度计算

空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度。空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟实践复杂度类似,也使用大O渐进表示法


时间复杂度和空间复杂度区别就是时间复杂度可以累加,空间不可以累加。

就是时间用了就是用了,不能回去,而空间用了,还能销毁,下次继续用!

实例1:


// 计算BubbleSort的空间复杂度?
void BubbleSort(int* a, int n)
{    
     assert(a);   
 for (size_t end = n; end > 0; --end)   
  {
     int exchange = 0;
     for (size_t i = 1; i < end;  ++i)    
         {
         if (a[i-1] > a[i])            
         {
         Swap(&a[i-1], &a[i]);
         exchange = 1;           
          }        
        }
    if (exchange == 0)
    break;  
  }
}

上面代码,包括形参,一共创建了5个变量,所以空间复杂度为:O(1)

实例2:


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

上面代码,malloc了n+1个空间,所以空间复杂度:O(n)

实例3:


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

递归调用了N次,开辟了N个栈帧,每个栈帧使用了常数个空间。空间复杂度为O(N)


有复杂度的算法练习题

相信你们都学会了,那就来刷个题吧!

3.1消失的数字OJ链接:https://leetcode-cn.com/problems/missing-number-lcci/


3.2 旋转数组OJ链接:https://leetcode-cn.com/problems/rotate-array/



目录
相关文章
|
3月前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
94 1
|
3月前
|
存储 算法 搜索推荐
【趣学C语言和数据结构100例】91-95
本文涵盖多个经典算法问题的C语言实现,包括堆排序、归并排序、从长整型变量中提取偶数位数、工人信息排序及无向图是否为树的判断。通过这些问题,读者可以深入了解排序算法、数据处理方法和图论基础知识,提升编程能力和算法理解。
81 4
|
3月前
|
存储 机器学习/深度学习 搜索推荐
【趣学C语言和数据结构100例】86-90
本文介绍并用C语言实现了五种经典排序算法:直接插入排序、折半插入排序、冒泡排序、快速排序和简单选择排序。每种算法都有其特点和适用场景,如直接插入排序适合小规模或基本有序的数据,快速排序则适用于大规模数据集,具有较高的效率。通过学习这些算法,读者可以加深对数据结构和算法设计的理解,提升解决实际问题的能力。
63 4
|
4天前
|
定位技术 C语言
c语言及数据结构实现简单贪吃蛇小游戏
c语言及数据结构实现简单贪吃蛇小游戏
|
22天前
|
搜索推荐 C语言
数据结构(C语言)之对归并排序的介绍与理解
归并排序是一种基于分治策略的排序算法,通过递归将数组不断分割为子数组,直到每个子数组仅剩一个元素,再逐步合并这些有序的子数组以得到最终的有序数组。递归版本中,每次分割区间为[left, mid]和[mid+1, right],确保每两个区间内数据有序后进行合并。非递归版本则通过逐步增加gap值(初始为1),先对单个元素排序,再逐步扩大到更大的区间进行合并,直至整个数组有序。归并排序的时间复杂度为O(n*logn),空间复杂度为O(n),且具有稳定性,适用于普通排序及大文件排序场景。
|
3月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
99 5
|
3月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
99 1
|
3月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
332 9
|
3月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
55 1
|
1月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
142 77

热门文章

最新文章