数据结构——时间复杂度和空间复杂度

在这里插入图片描述
1.算法效率
2.时间复杂度
3.空间复杂度
4. 常见时间复杂度以及复杂度oj练习

1.算法效率
1.1 如何衡量一个算法的好坏

如何衡量一个算法的好坏呢?比如对于以下斐波那契数的计算

long long Fib(int N)
{
if(N < 3)
return 1;
return Fib(N-1) + Fib(N-2);
}

我们看到虽然用递归的方式实现斐波那契很简单,但是简单一定代表效率高吗?
我们接着往下看。
1.2 算法的复杂度
算法在编写成可执行程序后,运行时需要耗费时间资源和空间(内存)资源 。因此衡量一个算法的好坏,一般
是从时间和空间两个维度来衡量的,即时间复杂度和空间复杂度。
时间复杂度主要衡量一个算法的运行快慢,而空间复杂度主要衡量一个算法运行所需要的额外空间。在计算
机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计
算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度。
1.3 复杂度在校招中的考察
在这里插入图片描述
我们可以看到在校招笔试的时候可能会遇到一些问题,就是它限制了时间和空间的复杂度,这无疑是加大了难度,所以我们现要了解什么是时间和空间复杂度,这样才能去写这道题。
2.时间复杂度
2.1 时间复杂度的概念
时间复杂度的定义:在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一
个算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知
道。但是我们需要每个算法都上机测试吗?是可以都上机测试,但是这很麻烦,所以才有了时间复杂度这个
分析方式。一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法
的时间复杂度。
即:找到某条基本语句与问题规模N之间的数学表达式,就是算出了该算法的时间复杂度。
// 请计算一下Func1中++count语句总共执行了多少次?

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--){++count;}

通过我们的精确计算,count总共经过了N^2+2*N+M.
Func1 执行的基本操作次数 :
N = 10 F(N) = 130
N = 100 F(N) = 10210
N = 1000 F(N) = 1002010

所以当N特别大的时候后面可以忽略掉。
所以上面的代码的时间复杂度是O(N^2).

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

2.2 大O的渐进表示法
大O符号(Big O notation):是用于描述函数渐进行为的数学符号。
推导大O阶方法:
1、用常数1取代运行时间中的所有加法常数。
2、在修改后的运行次数函数中,只保留最高阶项。
3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。
使用大O的渐进表示法以后,Func1的时间复杂度为O(N^2).
通过上面我们会发现大O的渐进表示法去掉了那些对结果影响不大的项,简洁明了的表示出了执行次数。
另外有些算法的时间复杂度存在最好、平均和最坏情况:
最坏情况:任意输入规模的最大运行次数(上界)
平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数(下界)
例如:在一个长度为N数组中搜索一个数据x
最好情况:1次找到
最坏情况:N次找到
平均情况:N/2次找到
在实际中一般情况关注的是算法的最坏运行情况,所以数组中搜索数据时间复杂度为O(N)

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

// 计算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);
}

这个count的准确次数是2*N+M
所以写成大O是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\n", count);
}

这里就要看前提是什么,如果不知道M和N的话,我们就可以写成O(M+N),如果M和N一样的话,可以写成O(N),如果N比M大的多,就可以写成O(N),反之则为O(M)。

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

常数项其实就是O(1)因为我们的计算机的运行速度特别快,每秒十几亿的速度,所以常数项都可以写成O(1).

// 计算strchr的时间复杂度?
const char * strchr ( const char * str, int character );

在这里插入图片描述
意思就是找一个字符,有就返回字符位置,没有就返回空指针,所以这肯定要遍历一遍我们的字符串,所以是O(N)。

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

冒泡排序我们写的第一个是它的趟数,然后进行前后进行比较,如果有n个数,那第一次要比较的是n-1次,接下来,比如我们是升序的话,最后一个数就排好了,并且是最大的一个数,所以第二次就可以忽略最后一个数的比较,接下来就是n-2,这样下去一直到1,所以将他们相加,是(N^2-N)/2,当N无穷大的时候,所以大O就可以写成O(N*N).最快只需要n-1次就可以了

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

我们的二分查找时间复杂度可快了,是效率非常高的一个排序。
它的算法时间复杂度是O(log2N),可以写成logN,底数是2.
为什么说他快呢,举个例子,比如我们要在14亿人中要找出有个人,最坏情况只要31次,第一次直接去掉了7亿人,第二次又是一半,所以效率快。

//计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N)
{if (0 == N)return 1;return Fac(N - 1) * N;
}

其实它递归了N次,那就很简单,就是O(N)。

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

通过计算分析发现基本操作递归了2N次,时间复杂度为O(2N),因为我们第一次是2的1次,后面就是2的2次,一直到2的n次,相加就可写成O(2^N),所以递归并不是效率特别高的算法有时候,但是它简洁。

今天的分享就到这里,我们下次再见

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/68225.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QT报表Limereport v1.5.35编译及使用

1、编译说明 下载后QT CREATER中打开limereport.pro然后直接编译就可以了。编译后结果如下图&#xff1a; 一次编译可以得到库文件和DEMO执行程序。 2、使用说明 拷贝如下图编译后的lib目录到自己的工程目录中。 release版本的重新命名为librelease. PRO文件中配置 QT …

R语言生存分析(机器学习)(1)——GBM(梯度提升机)

GBM是一种集成学习算法&#xff0c;它结合了多个弱学习器&#xff08;通常是决策树&#xff09;来构建一个强大的预测模型。GBM使用“Boosting”的技术来训练弱学习器&#xff0c;这种技术是一个迭代的过程&#xff0c;每一轮都会关注之前轮次中预测效果较差的样本&#xff0c;…

大模型时代和传统机器学习时代工具栈侧重点有所不同

大模型时代和传统机器学习时代工具栈侧重点有所不同 本章从企业训练模型、构建AI赋能应用的工作流视角出发,详解涉及的主要环节,并关注LLMOps和MLOps在流程上的侧重点差异。我们认为AI = Data + Code,历经数据准备、模型训练、模型部署、产品整合,分环节看: ► 数据准…

P4500Q22CLRP 半导体放电管 品牌厂家 现货直供

防浪涌过电压保护电路中&#xff0c;常用的过电压保护器件有&#xff1a;半导体放电管TSS、TVS瞬态抑制二极管、压敏电阻MOV、陶瓷气体放电管GDT&#xff0c;其中半导体放电管TSS和陶瓷气体放电管GDT属于开关型过压保护器件&#xff0c;压敏电阻MOV和TVS瞬态抑制二极管属于钳位…

使用Nginx解决跨域问题

前言&#xff1a; 项目是公司的老项目&#xff0c;只有部署在服务器上的时候&#xff0c;项目才可以正常运行&#xff08;接口是通的&#xff09;&#xff1b;现在需求&#xff1a;在现有的项目代码上进行修改&#xff0c;请求接口是第三方给的。接口是正常的&#xff0c;通过A…

pytest 用例运行方式

一、命令行方式运行 执行某个目录下所有的用例&#xff0c;符合规范的所有用例 进入到对应的目录,直接执行pytest; 例如需要执行testcases 下的所有用例; 可以进入testcases 目录; 然后执行pytest 进入对应目录的上级目录,执行pytest 目录名称/ ; ; 例如需要执行testcases 下…

【Java多线程学习7】Java线程池技术

线程池技术 一、什么是线程池 线程池顾名思义是管理一组线程的池子。当有任务要处理时&#xff0c;直接从线程池中获取线程来处理&#xff0c;处理完之后线程不会立即销毁&#xff0c;而是等待下一个任务。 二、为什么要使用线程池? 线程池的作用&#xff1f; 1、降低资源…

手机里视频太大怎么压缩?压缩教程分享

现在视频文件的体积越来越大了&#xff0c;动不动就是几个GB起步&#xff0c;如果后期再剪辑处理一下&#xff0c;更是会占据更多的设备空间了&#xff0c;还会导致我们传输受到限制&#xff0c;这时候就需要我们对视频进行压缩处理&#xff0c;下面给大家分享几个简单的方法&a…

领航优配:券商板块热度不减,华林证券涨停,中银证券等走高

券商板块15日午后再度走强&#xff0c;截至发稿&#xff0c;华林证券涨停&#xff0c;中银证券涨超7%&#xff0c;兴业证券涨超3%&#xff0c;东方财富、华泰证券、太平洋等涨逾2%。 组织表示&#xff0c;当前券商PB估值为1.36倍&#xff0c;位于2020年以来的34%分位点附近&…

ClickHouse(十九):Clickhouse SQL DDL操作-1

进入正文前&#xff0c;感谢宝子们订阅专题、点赞、评论、收藏&#xff01;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; &#x1f3e1;个人主页&#xff1a;含各种IT体系技术&#xff0c;IT贫道_Apache Doris,大数据OLAP体系技术栈,Kerberos安全认证-CSDN博客 &…

KAFKA第二课之生产者(面试重点)

生产者学习 1.1 生产者消息发送流程 在消息发送的过程中&#xff0c;涉及到了两个线程——main线程和Sender线程。在main线程中创建了一个双端队列RecordAccumulator。main线程将消息发送给RecordAccumulator&#xff0c;Sender线程不断从RecordAccumulator中拉取消息发送到K…

电脑屏幕闪烁?别慌!解决方法在这!

“我新买了一台电脑&#xff0c;还没用几天呢&#xff0c;就出现了电脑屏幕闪烁的情况&#xff0c;这让我感到很烦躁。有什么方法可以解决电脑屏幕闪烁的问题呢&#xff1f;” 使用电脑的过程中&#xff0c;我们不难发现电脑屏幕有时候会出现闪烁的情况&#xff0c;这会导致使用…