数据结构入门 — 时间复杂度、空间复杂度

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 数据结构_空间复杂度_时间复杂度讲解_常见复杂度对比本文介绍数据结构中的时间复杂度和空间复杂度一、算法效率二、时间复杂度1. 时间复杂度的定义2. 大O的渐进表示法3. 时间复杂度计算举例三、空间复杂度1. 空间复杂度的定义2. 空间复杂度计算举例四、常见复杂度对比1. 常见的时间复杂度及其对应的算法2. 常见复杂度的对比

前言

数据结构_空间复杂度_时间复杂度讲解_常见复杂度对比


本文介绍数据结构中的时间复杂度和空间复杂度

***文章末尾,博主进行了概要总结,可以直接看总结部分***


点点关注,后期持续更新系列文章


文章目录

前言

一、算法效率

二、时间复杂度

1. 时间复杂度的定义

2. 大O的渐进表示法

3. 时间复杂度计算举例

三、空间复杂度

1. 空间复杂度的定义

2. 空间复杂度计算举例

四、常见复杂度对比

1. 常见的时间复杂度及其对应的算法

2. 常见复杂度的对比

总结


一、算法效率

算法效率指的是算法在处理数据时所消耗的时间和空间资源的多少。


算法效率通常由时间复杂度空间复杂度来衡量。


时间复杂度: 表示算法在处理数据时所需要的运算次数与数据规模之间的关系。


空间复杂度: 表示算法在处理数据时所需要的存储空间与数据规模之间的关系。


算法效率的好坏直接影响到算法的执行速度和资源利用率,是算法设计时需要考虑的重要因素之一。



二、时间复杂度


1. 时间复杂度的定义

时间复杂度的定义:在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。


一个算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。但是我们需要每个算法都上机测试吗?是可以都上机测试,但是这很麻烦,所以才有了时间复杂度这个分析方式。


一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度。


即:找到某条基本语句与问题规模N之间的数学表达式,就是算出了该算法的时间复杂度。


举例说明:

// 请计算一下Func1中++count语句总共执行了多少次?voidFunc1(intN)
{
intcount=0;
for (inti=0; i<N; ++i)
    {                                   //外层循环n次for (intj=0; j<N; ++j)
        {
++count;                    //内层循环n次//n*n        }
    }
for (intk=0; k<2*N; ++k)
    {
++count;                    //2*N 次    }
intM=10;
while (M--)
    {
++count;                    //循环10次    }
printf("%d\n", count);
}

Func1 执行的基本操作次数 :

image.png


N = 10 F(N) = 130

N = 100 F(N) = 10210

N = 1000 F(N) = 1002010

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


使用大O的渐进表示法以后,Func1的时间复杂度为:

image.png

N = 10 F(N) = 100

N = 100 F(N) = 10000

N = 1000 F(N) = 1000000

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


2. 大O的渐进表示法

大O表示法是算法时间复杂度的渐进表示法,表示算法的运行时间在最坏情况下的渐进上限

大O符号(BigOnotation):是用于描述函数渐进行为的数学符号。

推导大O阶方法:

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

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

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


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

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

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

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


在实际中一般情况关注的是算法的最坏运行情况

需要注意的是,大O表示法只关注算法的渐进时间复杂度,而不关注算法的具体实现细节和常数项。因此,两个算法的时间复杂度可能相同,但它们的实际运行时间可能差别很大,这需要具体问题具体分析。


3. 时间复杂度计算举例

实例1:

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

实例1解析:

上述算法基本操作执行了2N+10次,通过推导大O阶方法知道,时间复杂度为 O(N)


实例2:

// 计算Func3的时间复杂度?voidFunc3(intN, intM)
{
intcount=0;
for (intk=0; k<M; ++k)
    {
++count;
    }
for (intk=0; k<N; ++k)
    {
++count;
    }
printf("%d\n", count);
}

实例2解析:

上述算法基本操作执行了M+N次,有两个未知数M和N,时间复杂度为 O(N+M)


实例3:

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

实例3解析:

上述算法基本操作执行了10次,通过推导大O阶方法,时间复杂度为 O(1)


实例4:

// 计算strchr的时间复杂度?constchar*strchr ( constchar*str, intcharacter );

实例4解析:

上述算法基本操作执行最好1次,最坏N次,时间复杂度一般看最坏,时间复杂度为 O(N)


实例5:

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

实例5解析:

上述算法基本操作执行最好N次,最坏执行了(N*(N+1)/2次,通过推导大O阶方法+时间复杂度一般看最坏,时间复杂度为 O(N^2)


实例6:

// 计算BinarySearch的时间复杂度?intBinarySearch(int*a, intn, intx)
{
assert(a);
intbegin=0;
intend=n-1;
// [begin, end]:begin和end是左闭右闭区间,因此有=号while (begin<=end)
    {
intmid=begin+ ((end-begin) >>1);
if (a[mid] <x)
begin=mid+1;
elseif (a[mid] >x)
end=mid-1;
elsereturnmid;
    }
return-1;
}

实例6解析:

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

ps:logN在算法分析中表示是底数为2,对数为N。有些地方会写成lgN。(建议通过折纸查找的方式讲解logN是怎么计算出的)


实例7:

// 计算阶乘递归Fac的时间复杂度?longlongFac(size_tN)
{
if (0==N)
return1;
returnFac(N-1) *N;
}

实例7解析:

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


实例8:

// 计算斐波那契递归Fib的时间复杂度?longlongFib(size_tN)
{
if (N<3)
return1;
returnFib(N-1) +Fib(N-2);
}

实例8解析:

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


三、空间复杂度


1. 空间复杂度的定义

空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度 。


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


注意:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定。


2. 空间复杂度计算举例

实例1:

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

实例1解析:

上面算法里用三个变量,为常数个变量

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


实例2:

// 计算Fibonacci的空间复杂度?// 返回斐波那契数列的前n项longlong*Fibonacci(size_tn)
{
if (n==0)
returnNULL;
longlong*fibArray= (longlong*)malloc((n+1) *sizeof(longlong));
fibArray[0] =0;
fibArray[1] =1;
for (inti=2; i<=n; ++i)
    {
fibArray[i] =fibArray[i-1] +fibArray[i-2];
    }
returnfibArray;
}

实例2解析:

上面算法使用malloc动态内存函数开辟了N个空间

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


实例3:

// 计算阶乘递归Fac的空间复杂度?longlongFac(size_tN)
{
if (N==0)
return1;
returnFac(N-1) *N;
}

实例3解析:

上述算法使用了递归

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


四、常见复杂度对比


1. 常见的时间复杂度及其对应的算法

image.png

2. 常见复杂度的对比

image.png

总结

时间复杂度和空间复杂度是用来衡量算法性能的两个重要指标。


时间复杂度表示算法的运行时间随着数据规模的增长而增长的趋势,通常用大O表示法来表示。时间复杂度越低,算法的效率越高。时间复杂度和数据规模之间的关系如下:


  • 常数时间:O(1)
  • 对数时间:O(log n)
  • 线性时间:O(n)
  • 线性对数时间:O(n log n)
  • 平方时间:O(n^2)
  • 立方时间:O(n^3)
  • 指数时间:O(2^n)


空间复杂度表示算法所需内存空间增长的趋势,同样用大O表示法来表示。空间复杂度越低,算法所需内存空间越少。空间复杂度和数据规模之间的关系如下:


  • 常数空间:O(1)
  • 线性空间:O(n)
  • 平方空间:O(n^2)


在实际应用中,我们要综合考虑时间复杂度和空间复杂度,找到一个性能和资源占用的平衡点,以实现最优的算法效果。时间复杂度和空间复杂度是用来衡量算法性能的两个重要指标。



如这篇博客对大家有帮助的话,希望 三连 支持 !!!

目录
打赏
0
0
0
0
70
分享
相关文章
数据结构与算法学习十:排序算法介绍、时间频度、时间复杂度、常用时间复杂度介绍
文章主要介绍了排序算法的分类、时间复杂度的概念和计算方法,以及常见的时间复杂度级别,并简单提及了空间复杂度。
134 1
数据结构与算法学习十:排序算法介绍、时间频度、时间复杂度、常用时间复杂度介绍
|
6月前
|
[数据结构] -- 时间复杂度和空间复杂度
[数据结构] -- 时间复杂度和空间复杂度
53 0
Nginx入门 -- 基本数据结构中之ngx_hash_t
Nginx入门 -- 基本数据结构中之ngx_hash_t
60 0
Nginx入门 -- 基本数据结构中之ngx_list_t,ngx_queue_t
Nginx入门 -- 基本数据结构中之ngx_list_t,ngx_queue_t
65 0
Nginx入门 -- 基本数据结构中之ngx_str_t,ngx_array_t
Nginx入门 -- 基本数据结构中之ngx_str_t,ngx_array_t
120 0
|
5月前
|
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
487 9
|
5月前
|
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
73 1
|
3月前
|
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
178 77
|
2月前
|
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
32 11