时间复杂度空间复杂度 力扣:转轮数组,消失的数字

news2024/10/7 6:39:39

1. 算法效率

  1. 如何衡量一个算法的好坏?
  2. 一般是从时间和空间的维度来讨论复杂度,但是现在由于计算机行业发展迅速,所以现在并不怎么在乎空间复杂度了
  3. 下面例子中,斐波那契看上去很简洁,但是复杂度未必如此
long long Fib(int N)
 {
 if(N < 3)
 return 1;
 return Fib(N-1) + Fib(N-2);
 }
  1. 摩尔定律,每两年硬件性能就会翻两倍,但是现在这个结论有些失效了,主要是因为计算机行业现在快处于瓶颈期了,很难再有突破
  2. 学习复杂度有什么用呢?
    1. 主要是在面试中和校招中考察。
    2. 其实再写一个算法的时候可以进行大概的估算

2. 时间复杂度

  1. 算法的时间复杂度是一个数学函数,定量描述了该算法的运行时间
  2. 每个机器的运行速度都不一样,不同的机器跑一样的代码,时间上会有差异
  3. 所以这个时候有了时间复杂度,时间复杂度计算的是程序执行的次数(大概的次数)
  4. 下面举个最简单的例子,下面代码的复杂度是多少?看时间复杂度,循环嵌套循环的复杂度就是 N^2
  5. 这个得出N^2,是因为在程序执行的时候,假设每执行100次,100次里的第一个循环就要执行100次,外层循环每次执行一次,里面的for循环就要执行100次
int main()
{
	int n = 100000,count = 0;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			count++;
		}
	}
	printf("%d", count);
	return 0;
}

2.1. 大0的渐进表示法

  1. 看下面图来说,当 N 值越来越大的时候,2 * N + 10的部分影响就很小了

  1. 因为计算机运行的速度很快,比如我这个电脑运行速度是3.10GHz,也就意味着,在一段时间周期内可以处理3.1亿次指令,对于电脑来说多处理十几万的次数可谓相当轻松。
  2. 大O渐进法,就是对影响结果最大的值进行估算,只要保留最高项就行
  3. 再举个例子 1.N^2 + 2n; 2.N + 100; 3. N^3 。当N在10和100像这种比较小的数的时候,此时他们时间复杂度是差不多的。

2.2. clock 函数 <time.h>

  1. 返回程序消耗的处理器时间 ,单位 毫秒(ms)。
  2. 我的电脑,处理10亿次指令,用2075ms,2秒多。大家有兴趣,也可以用这个函数去试试

2.3. 例题

  1. 0 ms表示,运行时间小于1 ms
  2. 那么下面的时间复杂度是多少呢?时间复杂度计算的是大概的执行次数。是 F(N) = 2 * N + M; 用大O渐进法就是 O(N);
  3. 得出O(N),因为我们要省略对结果影响不大的值
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);
}

2.3.1. 第二题

  1. 最好的情况就是一方远大于另一方

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

2.3.2. 第三题

  1. 那么这里是多少?,这个O(1), 这里意思不是执行1次,也不是执行时间低于1ms,而是这里的 1,表示常数次(常数就是1,2,3,4,5,...)
void Func4(int N)
 {
    int count = 0;
    for (int k = 0; k < 100; ++ k)
    {
        ++count;
    }
    printf("%d\n", count);
 }

2.4. 大O符号(Big O notation):是用于描述函数渐进行为(大概)的数学符号

  1. 推导大O阶方法:
    1. 用常数1代替运行时的所有加法常数
    2. 去掉其他影响不大的项,只要保留最高阶项
    3. 如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。
  2. 本质:计算算法复杂度(次数) 属于哪个量级(level)
  3. 假如有两个富豪,一个有2000亿,另一个有3000亿,都去买咖啡喝,不管是便宜的200左右的,还是5000的,就算是50w的。富豪都买的起,意思想表达的就是富豪们都是属于一个级别的你买起,那么他也买的起,这点钱就无足轻重了

常见算法复杂度如下:

2.5. 复杂度的最好、平均和最坏

  1. 最坏情况:任意输入规模的最大运行次数(上限)
  2. 平均情况:输入任意数,期望的运行次数
  3. 最好情况:输入任意数,最小的运行次数(下限)

例如:在长度N的数组中查找一个数据

  1. 最好的情况 O(1),最坏的情况O(N),平均情况 N/2.

2.6. 例题,消失的数字

  1. 此题共提供两种思路
    1. 第1种:用按位或,先按位或上
    2. 第2种:所有项数相加,然后依次减去数组里的值,减去剩下来的值就是,单生狗

//方法1
int missingNumber(int* nums, int numsSize){
    int one = 0,second = 0;
    for(int i = 0;i <= numsSize;++i)//numsSize <= 多一个
    {
        one ^= i;
    }
    for(int j = 0;j < numsSize;j++)
    {
        second ^= nums[j];
    }
    return one ^ second;
}
//方法2
int missingNumber(int* nums, int numsSize){
    int N = numsSize;//等差数列项数 result
    int ret = (0 + N) * (N + 1) / 2; // N + 1 是因为确实的那个项
    for(int i = 0;i < numsSize; i++)
    {
        ret -= nums[i];//ret 项数的总和,依次减去数组内的数
    }
    return ret;
}
  1. 图中是挨个异或,这里代码中直接先将0 - n,全部异或。然后再异或原数组,最有将两个异或,就得到了这个数

2.7. 轮转数组

  1. 循环条件是 left < right; //不用写 left <= right,当时单数的时候,交不交换都一样。,因为两边向中间靠拢交换
  2. 要画图,画清楚图代码都是水到渠成了
  3. 一定要注意,数组下标绝对不能是负数

void reverse(int* nums,int left, int right)
{
    while(left < right)//比较的下标值
    {
        int tmp = nums[left];
        nums[left] = nums[right];
        nums[right] = tmp;
        left++;
        right--;
    }
}
void rotate(int* nums, int numsSize, int k) {
    k %= numsSize;// k % numsSize 6 % 7 = 6
    reverse(nums,0,numsSize - k - 1);
    reverse(nums,numsSize - k,numsSize - 1);
    reverse(nums,0,numsSize - 1);
}

2.7.1. 还有一种方法,额外开辟空间

  1. 就是你们常说的用空间换时间,这个的空间复杂度是O(N),为什么是N,因为malloc开辟的空间是未知的
  2. 只要根据上面画的图,稍微推断一下就知道了
  3. 如果还不知道memcpy怎么使用的可以去看看 ,这篇博客C语言的内存函数
void rotate(int* nums, int numsSize, int k) {
    k %= numsSize;
    int* numsby = (int*)malloc(sizeof(int) * numsSize);
    //拷贝到新空间的前三个
    memcpy(numsby,nums + (numsSize - k),sizeof(int) * k);
    //把剩下的拷贝
    memcpy(numsby + k,nums,sizeof(int) * (numsSize - k));
    //把新空间的拷贝会nums
    memcpy(nums,numsby,sizeof(int) * numsSize);
    free(numsby);
    numsby = NULL;
}

2.8. logN复杂度

int main()
{
    int n = 8;
    int count = 0, i = 1;
    for (i = 1; i < n; i *= 2)
    {
        count++;
    }
    printf("%d\n", count);
    return 0;
}
  1. 二分查找的每次区间变化是 N / 2,每次查找都是N /2 /2 /2...
int main()
{
    int arr[] = { 1,2,3,4,5,6,7,8,9 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    int left = 0, right = sz - 1;

    int k = 7;
    while (left <= right)//为啥有等于因为,为单数时还有一个数需要查找
    {
        int mid = left + (right - left) / 2;
        if (arr[mid] < k)//左半没有我需要的值
            left = mid + 1;
        else if (arr[mid] > k)//被查找的值小于中间值,此时说明右半区间没有需要的值
            right = mid - 1;
        else
        {
            printf("%d", mid);
            break;
        }
        left++;
        right--;
    }
    return 0;
}

2.9. 乘阶的复杂度

  1. 乘阶的复杂的是 N + 1,算的是函数的调用次数总和 ,所以就是O(N)。
// 计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N)
 {
    if(0 == N)
        return 1;
    
    return Fac(N-1)*N;
 }
  1. 如果乘阶里面还有计算呢?
long long Fac(size_t N)
 {
    if(0 == N)
        return 1;
    for(int i; i < N;i++)
    {
        ;
    }
    return Fac(N-1)*N;
 }
  1. 这个时候每调用一次,for循环就会打印 N次,这个消耗也要算进去,而且这个是递归调用,每次调用自身都会打印 n - 1次的数据,直到满足结束条件。
  2. 所以在这个递归调用里,复杂度是 O(N^2);。所有递归次数累加

2.10. 斐波那契的复杂度

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

int main()
{
	//斐波那契
	int ret = Fib(40);
	printf("%d ", ret);
	return 0;
}
  1. 对此上面的方法只有理论意义,并不具有实际意义
  2. 所以我们要用迭代的方法O(N),来解决,这个方法更好
int main()
{
	//斐波那契
	//迭代的方法,因为斐波那契前两项都是1
	unsigned long long x1 = 1;
	unsigned long long x2 = 1;
	unsigned long long x3 = 0;
	int n = 1150;
	for (int i = 3; i <= n; i++)
	{
		x3 = x1 + x2;
		x1 = x2;
		x2 = x3;
	}
	printf("%lld", x3);
	return 0;
}
  1. 这种迭代的方法还是不够好,算较小的数还行,数字大了还是不行,会到类型上限
  2. 可以用字符串存储,只要空间够大,多大的数都能存储。不过还没学到,下次一定!!

3. 空间复杂度

  1. 一个算法重点关注时间复杂度,不太关注空间复杂度,除非是嵌入式那些有大小限制的设备上。
  2. 空间复杂度算的是变量的个数,因为每个变量的差异不大,为什么没有什么差异?,举个例子
    1. 1GB = 1024MB;1MB = 1024KB 1 KB = 1024 Byte ;1Byte = 8bit;
    2. 一个MB的空间都有这么大了,还在乎这么点空间吗?
  3. 空间复杂度使用的也是大O渐进法。
  4. 可以来看看实例 ,函数的形参部分的变量不算个数,算的是函数内额外的变量个数
  5. 在此题目中冒泡排序里面,发现只开辟了三个变量,空间复杂度是O(1)
void bbu(int a[], int len)
{
	for (int i = 0; i < len - 1; i++)
	{
		for (int j = 0; j < len - i - 1; j++)
		{
			if (a[j] < a[j + 1])
			{
				int tmp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = tmp;
			}
		}
	}
}

3.1. 空间复杂度 O(N)

  1. 实际上空间复杂度比时间复杂度更加容易计算
  2. 常见的空间复杂度只有三个,O(1) O(N) O(N^2);但是也有其他的
  3. 举个空间复杂为O(N)的例子
  4. 每次函数的调用都会创建一个栈帧,创建了多少个栈帧就是多大的空间,会调用N次,O(N)了
// 计算阶乘递归Fac的空间复杂度?
long long Fac(size_t N)
{
    if(N == 0)
        return 1;
    return Fac(N-1)*N;
}

总结:个人觉得可能会不太详细,但是重点部分都没拉下

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BAPI_PR_CHANGE how to add account assignment information for service line,如何解决?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

数据结构(十)----图

目录 一.图的概念 1.图的定义 2.图的类别 3.图的性质 4.几种特殊形态的图 二.图的存储结构 1.邻接矩阵&#xff08;顺序存储&#xff09; 2.邻接表&#xff08;顺序链式存储&#xff09; 3.十字链表 4.邻接多重表 四.图的遍历 1.广度优先遍历&#xff08;BFS&#…

Elasticsearch 数据聚合

Bucket聚合&#xff08;桶聚合&#xff09; 对文档做分组&#xff0c;aggs 按照文档字段值或日期进行分组&#xff0c;能参与分词的字段不能做聚合&#xff0c;如text类型的字段 例如&#xff1a;根据城市名称做聚合&#xff0c;也就是城市名称对数据进行分组统计。可以加qu…

Python数据分析案例43——Fama-French回归模型资产定价(三因子/五因子)

案例背景 最近看到要做三因子模型的同学还挺多的&#xff0c;就是所谓的Fama-French回归模型&#xff0c;也就是CAMP资本资产定价模型的升级版&#xff0c;然后后面还升级为了五因子模型。 看起来眼花缭乱&#xff0c;其实抛开金融资产定价的背景&#xff0c;从机器学习角度来…

2024年3月Scratch图形化编程等级考试(三级)真题试卷

2024年3月Scratch图形化编程等级考试&#xff08;三级&#xff09;真题试卷 选择题 第 1 题 Scratch运行程序后&#xff0c;角色一定不会说出的数字是&#xff1f;&#xff08; &#xff09; A.2 B.4 C.6 D.8 第 2 题 Scratch角色初始位置如下图所示&#xff0c;右图…

Spring Boot与OpenCV:融合机器学习的智能图像与视频处理平台

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

spring框架学习记录(2)

文章目录 注解开发bean相关注解开发定义bean纯注解开发纯注解开发中bean的管理 依赖注入相关依赖注入第三方bean管理第三方bean依赖注入 AOP(Aspect Oriented Programming)面向切面编程AOP简介AOP核心概念AOP工作流程AOP切入点表达式通知类型AOP通知获取数据 注解开发 bean相关…

大语言模型中的第一性原理:Scaling laws

大语言模型的尺度定律在大语言模型的训练过程中起到了非常重要的作用。即使读者不参与大语言模型的训练过程&#xff0c;但了解大语言模型的尺度定律仍然是很重要的&#xff0c;因为它能帮助我们更好的理解未来大语言模型的发展路径。 1. 什么是尺度定律 尺度定律&#xff08…

现代循环神经网络(GRU、LSTM)(Pytorch 14)

一 简介 前一章中我们介绍了循环神经网络的基础知识&#xff0c;这种网络 可以更好地处理序列数据。我们在文本数据上实现 了基于循环神经网络的语言模型&#xff0c;但是对于当今各种各样的序列学习问题&#xff0c;这些技术可能并不够用。 例如&#xff0c;循环神经网络在…

【错题集-编程题】十字爆破(预处理 + 模拟)

牛客对于题目链接&#xff1a;十字爆破 (nowcoder.com) 一、分析题目 暴力模拟会超时。 预处理&#xff0c;先把每一行以及每一列的和存起来。 模拟即可&#xff0c;但是由于数据量过⼤&#xff0c;我们可以提前把每⼀⾏以及每⼀列的和存起来&#xff0c;⽅便统计总和。 二、代…

(centos)yum安装mysql8.4

1.MySQL官方已经提供了适用于不同Linux发行版的预构建软件包&#xff0c;包括适用于CentOS的Yum Repository MySQL :: MySQL Community Downloads 2.在/usr/local文件夹下创建mysql文件夹&#xff0c;将下载的rpm文件放到目录下 3.执行安装命令 yum install mysql-community-…

思科防火墙查如何查看现有ipsec隧道信息

环境&#xff1a; 思科ASA5555 问题描述&#xff1a; 思科防火墙查如何看现有ipsec隧道信息 解决方案&#xff1a; 1.进入特权模式&#xff1a; enable 查看isakmp信息 show crypto isakmp sa2.查看ipsec信息 show crypto ipsec sa上述命令将显示当前的ISAKMP安全关联…

leetCode69. x 的平方根

leetCode69. x 的平方根 题目思路 常见的二分法模板&#xff08;背过就行&#xff0c;模板而已&#xff09; // 区间[L,R]被划分为[L,mid]和[mid 1, R]时使用这个模板 int bsearch_1(int l, int r){while(l < r){int mid l r >> 1;if(check(mid)) r mid; //che…

08 IRF技术 华三交换机实现

IRF 详细介绍 我知道 AI IRF 技术是指集成路由功能(Integrated Routing and Bridging)技术,是惠普(Hewlett Packard)公司开发的一种基于硬件的虚拟化技术。IRF 技术可以将多台物理设备组合成一个逻辑设备,实现设备的高可用性和灵活性。 IRF 技术主要有以下特点: 1. …

【强训笔记】day9

NO.1 思路&#xff1a;利用两个string&#xff0c;一个输入数据&#xff0c;一个做逗号处理&#xff0c;如果该字符的位数减去下标减去1等于3的倍数的话&#xff0c;该位置就插入逗号。 代码实现&#xff1a; #include<iostream> #include<string> using names…

Redis事务,管道,发布订阅

Redis事务 redis事务本质上是一组命令的集合,按照顺序串行化执行命令而不被其他命令打断 redis事务开启后将要执行的命令放到事务队列中,提交事务后一次性顺序排他地执行所有命令 关键词:单线程,无隔离级别,不保证原子性,排他性,顺序性 要注意和mysql的acid进行区分 怎么用…

C++:智能指针(RAII思想)

目录 1、什么是智能指针&#xff1f; 2、为什么需要智能指针 3、RAII思想及智能指针的原理 4、智能指针的发展 4.1 auto_ptr 4.2 unique_ptr 4.3 share_ptr 5、share_ptr的模拟实现 6、循环引用问题 7、share_ptr中的自定义删除器 1、什么是智能指针&#xff1f; 智能…

java spring 09 Bean的销毁过程

1.Bean销毁是发送在Spring容器关闭过程中的 AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class);UserService userService (UserService) context.getBean("userService");userService.test();// 容器关闭cont…

LeetCode题练习与总结:最大矩形--85

一、题目描述 给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵&#xff0c;找出只包含 1 的最大矩形&#xff0c;并返回其面积。 示例 1&#xff1a; 输入&#xff1a;matrix [["1","0","1","0","0"],[&quo…

C++入门系列-类对象模型this指针

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 类对象模型 如何计算类对象的大小 class A { public:void printA(){cout << _a << endl;} private:char _a; }; 算算看&#xff0c;这个类的大小是多少 我们知道…