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

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.mfbz.cn/a/592237.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于改进暗原色先验和颜色校正的水下图像增强,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

【OpenNJet下一代云原生之旅】

OpenNJet下一代云原生之旅 1、OpenNJet的定义OpenNJet架构图 2、OpenNJet的特点性能无损动态配置灵活的CoPilot框架支持HTTP/3支持国密企业级应用高效安全 3、OpenNJet的功能特性4、OpenNJet的安装使用编译安装配置yum源创建符号连接修改配置编译 5、通过 OpenNJet 部署 WEB SE…

数字化战略|数字化建设总体规划蓝图PPT(建议收藏)

摘要 这份头部咨询公司关于数字化转型的报告为企业管理者和技术人员提供了一份详尽的数字化转型指南。报告从战略出发&#xff0c;详细阐述了数字生态体系建设、数字化核心方案构建、管理协同能力提升以及数据集中管理和应用能力增强等关键环节。对于从业者而言&#xff0c;报…

CogVLM/CogAgent环境搭建推理测试

引子 对于多模态大语言模型&#xff0c;一直没有怎么接触。刚巧一朋友有问到这方面的问题&#xff0c;也就顺手调研下。智谱AI的东西一直以来&#xff0c;还是很不错的。ChatGLM的忠实fans&#xff0c;看到白嫖网站github上有他们开源的多模态CogVLM/CogAgent&#xff0c;那就…

【JAVA项目】基于SSM的【电动车智能充电服务平台】

技术简介&#xff1a;采用SSM技术、MYSQL等技术实现。 系统简介&#xff1a;电动车智能充电服务平台实现了首页、个人中心、用户管理、充电桩管理、电池商品管理、托送服务管理、我的钱包管理、充值信息管理、消费信息管理、购买订单管理、配送信息管理、服务订单管理、系统管理…

43.139.152.26 C07L01P02 数字8

题目描述 不超过 N 位的正整数中包含有多少数字 8 &#xff1f; 输入格式 一行 1 个正整数 N &#xff0c;范围 [1,16]。 输出格式 一个整数。 输入数据 1 2输出数据 1 20样例解释 8,18,28,38,48,58,68,78,88, 98 与 80,81,82,83,84,85,86,87,89 这些数中有 8 &#xff0…

菜鸡学习netty源码(四)—— EventLoop

1.概述 我们前面进行过分析,channel为netty网络操作的抽象类,EventLoop负责处理注册到其上的Channel处理的I/O事件;EventLoopGroup是一个EventLoop的分组,它可以获取到一个或者多个的EventLoop对象。 2.类关系图 NioEventLoopGroup的类继承图,蓝色部分为对应的java类,绿…

使用Jellyfin创建媒体库

目录 前言 1.介绍Jellyfin 2.安装 3.设置时注意点 4.效果 5.内存占用 前言 分为客户端和服务器端&#xff0c;这里讲的是服务器端的安装 1.介绍Jellyfin Jellyfin 是一个免费开源的媒体服务器软件&#xff0c;它允许您管理和播放您的媒体文件。这些媒体文件可以包括电…

crossover软件是干嘛的 CrossOver软件好用吗 crossover最新2024使用方法教程 MacBook怎么安装exe软件

CrossOver软件是干嘛的 CrossOver由CodeWeavers公司开发&#xff0c;目的是使linux和Mac OS X操作系统和windows系统兼容。这样使用户可以将windows系统上的应用在linux或Mac os上运行。 CrossOver让您可以在Mac和Linux系统上运行Microsoft Windows应用&#xff0c;不必购买W…

第19章 基于质量特性的测试技术

一、功能性测试 &#xff08;一&#xff09;测试方法 等价类边界值法因果图法判定表法场景法 &#xff08;二&#xff09;用例 1、正常用例 2、异常用例 &#xff08;三&#xff09;完备性 1、功能覆盖率 2、X1-A/B 功能覆盖率X&#xff1a;软件实际功能覆盖文档中所有…

Linux的socket详解

一、本机直接的进程通信方式 管道&#xff08;Pipes&#xff09;&#xff1a; 匿名管道&#xff08;Anonymous pipes&#xff09;&#xff1a;通常用于父子进程间的通信&#xff0c;它是单向的。命名管道&#xff08;Named pipes&#xff0c;也称FIFO&#xff09;&#xff1a;允…

2023第十四届蓝桥杯国赛C/C++ 大学 A 组 圆上的连线

思路&#xff1a;很显然总的方案数等于挑选偶数点的方案数乘以对应偶数点的连线方案数之和&#xff0c;挑选偶数点的方案数靠组合数得出&#xff0c;偶数点的连线方案数就是个卡特兰数。具体为什么是卡特兰数&#xff0c;可以任选一个点&#xff0c;枚举这个点所连边的位置&…

Linux搭建sqlilabs靶场

提前准备&#xff1a; 文章中所使用到的Linux系统&#xff1a;Ubantu20.4sqlilabs靶场下载地址&#xff1a;GitHub - Audi-1/sqli-labs: SQLI labs to test error based, Blind boolean based, Time based. 一. 安装phpstudy phpstudy安装命令&#xff1a;wget -O install.sh h…

python爬虫实战

import requests import json yesinput(输入页数&#xff1a;) yesint(yes)headers {"accept": "application/json, text/plain, */*","accept-language": "zh-CN,zh;q0.9","content-type": "application/json",…

仿知乎网站问答源码,开源版

仿知乎网站问答源码&#xff0c;开源版 需要一定动手能力 发文章&#xff0c;发视频&#xff0c;发想法&#xff0c;提问回答&#xff0c;注册登录 开发环境 使用技术&#xff1a;springbootthymeleafRedis&#xff1b; 开发环境&#xff1a;tomcat8.0&#xff0c;jdk8.0, ID…

RKNN Toolkit2 工具的使用

RKNN Toolkit2 是由瑞芯微电子 (Rockchip) 开发的一套用于深度学习模型优化和推理的工具。它主要面向在瑞芯微SoC上进行AI应用开发&#xff0c;但也可以用于PC平台进行模型的转换、量化、推理等操作。它支持将多种深度学习框架的模型&#xff08;如Caffe, TensorFlow, PyTorch等…

如何优雅的分析你的微信朋友圈和聊天记录

微信朋友圈、个人聊天记录、微信群聊天记录&#xff1a; 蓝奏云&#xff1a;链接:​www.lanzoub.com/b00rn0g47e 密码:9hww

SPARC VScode EIDE GDB 使用配置

前言 搞了多年的SPARC 最近接触了VSCODE插件感觉好用。想想看不是能方便调试和编译SPARC&#xff0c;决定使用开源的SPARC仿真环境和编译器来试试。感觉的却不错&#xff0c;借此献给使用SPARC的朋友们。安装 1.找微软官方的下载VSCODE. 2.电机左边的方块形状的图标&#xff0…

周鸿祎成了中国新能源汽车的顶流IP

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 以后开公司创业&#xff0c;老板自己做网红是标配。 这句话是我在看了红衣大叔这段时间的操作后发的朋友圈。 短短半年时间(2023.12—2024.5)&#xff0c;周鸿祎就从一名传统互联网企业家变成了新能源汽车顶流IP…

C++相关概念和易错语法(10)(定位new、模板)

1.定位new 我们使用类来实例化对象&#xff0c;开辟空间的时候会自动去调用它的构造函数。但在那篇博客我就特意强调过&#xff0c;使用a.A()的方式是错误的&#xff0c;A()根本不会被识别为一个构造函数&#xff0c;而会被识别为A类型。因此我们要注意最好在实例化对象&#…
最新文章