数据结构——复杂度

news2024/9/24 3:21:37

   

总有一天你要一个人,再暗夜中,向那座桥走过去

文章目录

一、算法的复杂度

考察形式范例

二、算法的时间复杂度

大O的渐进表示法

常见的复杂度对比

例题:消失的数字

题目的三种思路

1.排序+遍历

2.减法

3.单身狗思想

三、空间复杂度

大O的渐进表示法

常见复杂度比较

四、复杂度判断总结


  大家好,我是纪宁。

  上篇文章已经为大家介绍了数据结构与算法,相信看过的人已经大值了解数据结构与算法了。在解决一个问题的时候,我们通常会使用各式各样的算法,那么,如何衡量一个算法的性能好坏或者效率高低呢?这里我们就要学习复杂度的概念。

  本文在空间复杂度的求解处提到了函数栈帧这个概念,不懂的老铁可以去看博主肝了好久的老作品 从汇编代码探究函数栈帧的创建和销毁的底层原理

  还有在时间复杂度和空间复杂度中都提到的冒泡排序算法

一、算法的复杂度

  算法在编写成可执行程序后,运行时需要耗费时间资源和空间(内存)资源 ,因此衡量一个算法的好坏,一般是从时间空间两个维度来衡量的,即时间复杂度和空间复杂度

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

考察形式范例

leetcode

腾讯面试题、剑指offer 

二、算法的时间复杂度

  时间复杂度的定义:在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。它可以算出一个理论时间值,这个值与与其中语句的执行次数成正比例。那么算法的时间复杂度,通俗来讲,就是算法中的基本操作的执行次数。

大O的渐进表示法

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

推导大O阶方法:

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

2、在修改后的运行次数函数中,只保留最高阶项(取极限后对记过影响最大的一项)

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

4、时间复杂度以最坏情况为准(如在数组中搜索数组,时间复杂度为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);
}

  这段代码关键语句执行了 2N+10 次,只看最高项,为2N,再除去常数2,所以时间复杂度为O(N)

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)

void Func4(int N)
{
	int count = 0;
	for (int k = 0; k < 100; ++k)
	{
		++count;
	}
	printf("%d\n", count);
}

  这段代码关键语句执行了100次,为常数次,所以时间复杂度为O(1)

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-1次,第二次冒泡要比较 n-2 次......最后一次冒泡只需要比较 1 次,一共需要n趟比较。那么关键语句的执行次数为 (n-1)+(n-2)+(n-3)......+2+1=n*n/2(等差数列求和),所以冒泡排序的时间复杂度为 O(N^2)

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 - 1;
		else
			return mid;
	}
	return -1;
}

  这段代码为二分查找算法,以最快情况考虑(最后一个元素才找到或者没找到),二分查找时间复杂度计算比较难,我画图来解释一下。

为了方便书写,我们通常将时间复杂度为 以2为底的对数简写为 O(logN) 

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

  这段代码是关于斐波那锲数列的递归解法,斐波那锲数列的递归法,用过的人都知道,只有理论意义,越递归,重复工作越多,越复杂,所以没有太大的实际意义。

当N=0的时候,一共进行了2^N数量级次递归语句的执行,所以时间复杂度为log(2^N)

例题:消失的数字

题目

数组nums包含从0n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?

示例 :

输入:[3,0,1]  

输出:2

示例 2:

输入:[9,6,4,2,3,5,7,0,1]   

输出:8

题目的三种思路
1.排序+遍历

  先排序,如果发现有个数的下一个数减这个数不等于1,那么这个数的下一个数就是‘消失的数字’。排序如果采用冒泡排序的话,时间复杂度会不合适,所以采用快速排序算法。快速排序的时间复杂度是 O(logN*N),依然不符合时间复杂度要求大家可以自己去尝试优化一下排序算法。

2.减法

  先用等差数列公式计算 1-n的值,再用这个值减去遍历数组的加和,得到的就是那个消失的数字。时间复杂度为O(N),符合要求。

int missingNumber(int* nums, int numsSize)
{
	int N = numsSize;
	int ret = N * (N + 1) / 2;//等差数列公式
	for (int i = 0; i < N; i++)
	{
		ret -= nums[i];
	}
	return ret;
}
3.单身狗思想

  先用1-n 的所有值异或,再用结果异或数组中的所有值,相同的数字可以相互配对,异或的值等于0,所以就剩下一个‘消失的数字’对应的数,这个数就是消失的数字。

int missingNumber(int* nums, int numsSize)
{
	int N = numsSize;
	int x = 0;
	for (int i = 0; i <= N; i++)
	{
		x ^= i;
	}
	for (int i = 0; i < N; i++)
	{
		x ^= nums[i];
	}
	return x;
}

三、空间复杂度

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

  空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度与定义变量的个数(额外开辟的空间数),计算规则也与时间复杂度相差无几。但函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显式申请的额外空间(函数栈帧申请的次数)来确定。

大O的渐进表示法

  空间复杂度的大O渐进表示法通过计算额外开辟的空间数和函数栈帧的申请次数即可。

常见复杂度比较

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

因为冒泡排序中只创建了 exchange 这一个额外空间的变量,所以空间复杂度就是O(1)

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

这段代码开辟了(n+1)个额外空间,所以空间复杂度为O(N)

long long Fac(size_t N)
{
	if (N <3)
		return 1;
	return Fib(N - 1) * Fib(N-2);
}

  这段代码为斐波那契额数列的递归解法,这个解法时间复杂度达到了O(2^N),而空间复杂度却是O(N),因为它的函数栈帧其实只开辟了N次。为什么呢?真实情况是函数进入Fib(N-1)后,暂时是不会进入同行语句中的Fib(N-2)的,当Fib(N-1)递归结束后(一共开辟并释放了N次函数栈帧),程序才开始进入那一行的Fib(N-2)函数中,因为Fib(N-1)中已经开辟了很多空间,虽然已经还给了操作系统,但Fib(N-2)中所开辟的函数栈帧依然是在曾经Fib(N-1)的那块空间上,所以并没有再多余浪费空间开辟函数栈帧。

四、复杂度判断总结

这个表格大家看一下应该可以轻易理解并记住的,主要是掌握时间和空间复杂度的计算。

52101314O(1)常数阶

3n+4

O(N)线性阶
3n^2+4n+5O(N^2)平方阶
3log(2)n+4O(logN)对数阶
2n+2nlog(2)n+14O(NlogN)NlogN阶
n^3+2n^2+4n+6O(N^3)立方阶
2^nO(2^N)指数阶

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

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

相关文章

UE5、CesiumForUnreal加载无高度地形

文章目录 1.实现目标2.实现过程3.参考资料1.实现目标 在UE5中,CesiumForUnreal插件默认的地形都是带高度的,这里加载没有高度的地形,即大地高程为0,GIF动图如下: 2.实现过程 参考官方的教程,下载无高度的DEM,再切片加载到UE中。 (1)下载无高度地形DEM0。 在官方帖子…

用合成数据训练托盘检测模型【机器学习】

想象一下&#xff0c;你是一名机器人或机器学习 (ML) 工程师&#xff0c;负责开发一个模型来检测托盘&#xff0c;以便叉车可以操纵它们。 ‌你熟悉传统的深度学习流程&#xff0c;已经整理了手动标注的数据集&#xff0c;并且已经训练了成功的模型。 推荐&#xff1a;用 NSDT设…

day58 单调栈

单调栈 使用场景&#xff1a;通常是一维数组&#xff0c;要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置 本质&#xff1a;空间换时间 三个判断条件&#xff1a; 当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况 当前遍历的元素T[i]等于栈顶元素T[st.to…

tinkerCAD案例:24.Tinkercad 中的自定义字体

tinkerCAD案例&#xff1a;24.Tinkercad 中的自定义字体 原文 Tinkercad Projects Tinkercad has a fun shape in the Shape Generators section that allows you to upload your own font in SVG format and use it in your designs. I’ve used it for a variety of desi…

JavaSE第二次考试复盘

sleep是线程类&#xff08;Thread&#xff09;的方法&#xff0c;导致此线程暂停执行指定时间&#xff0c;给执行机会给其他线程&#xff0c;但是监控状态依然保持&#xff0c;到时候会自动恢复。调用sleep不会释放对象锁wait是Object类的方法&#xff0c;对此对象调用wait方法…

AI 能在哪些方面提高普通的程序员的效率

一、提高编码效率 1、起名字&#xff1a;变量名、函数名、类名、表名、数据库名(相信每个程序员都因为起名字掉了不少头发) 2、写简单的工具函数代码、结构转换代码&#xff1a;比如求2个数组的元素交集&#xff1b;把类转换成B类&#xff0c;原来需要一个字段一个字段的手动赋…

用LangChain开源框架实现知识机器人

前言 Large Language Models (LLMs)在2020年OpenAI 的 GPT-3 的发布而进入世界舞台 。从那时起&#xff0c;他们稳步增长进入公众视野。 众所周知 OpenAI 的 API 无法联网&#xff0c;所以大家如果想通过它的API实现联网搜索并给出回答、总结 PDF 文档、基于某个 Youtube 视频…

前端学习——Vue (Day6)

路由进阶 路由的封装抽离 //main.jsimport Vue from vue import App from ./App.vue import router from ./router/index// 路由的使用步骤 5 2 // 5个基础步骤 // 1. 下载 v3.6.5 // 2. 引入 // 3. 安装注册 Vue.use(Vue插件) // 4. 创建路由对象 // 5. 注入到new Vue中&…

【Python】批量修改文件名

对指定文件夹中的文件名称修改&#xff1a; import os#文件路径 path D:\大明风华[第01-62集]#获取文件列表 file os.listdir(path)#print(file)#原文件名是 大明风华.Ming.Dynasty.2019.E01.2160p.60FPS.WEB-DL.H265.10bit.AAC-SeeTV.mp4 #预期修改后文件名 大明风华.E01.…

C语言基础入门详解二

前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂&#xff0c;风趣幽默"&#xff0c;感觉非常有意思,忍不住分享一下给大家。 &#x1f449;点击跳转到教程 一、C语言多级指针入门 #include<stdio.h> #include<stdlib.h>/**多级指针…

【报错1】无法找到模块“element-plus/dist/locale/zh-cn.mjs”的声明文件。

报错&#xff1a;无法找到模块“element-plus/dist/locale/zh-cn.mjs”的声明文件。“e:/codeAll/webProject/Project/vue_ts/project727/node_modules/element-plus/dist/locale/zh-cn.mjs”隐式拥有 "any" 类型。 如果“element-plus”包实际公开了此模块&#x…

Run主启动类的详解

package com.kuang.HelloSpringBoot;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;//本身就是spring的一个组件,也就是注册的bean SpringBootApplication public class HelloSpringBootApplicati…

使用LangChain构建问答聊天机器人案例实战(二)

使用LangChain构建问答聊天机器人案例实战 逐行解读和验证全生命周期Prompting 现在我们使用GPT-4作为语言模型的驱动力,这个模型将成为整个应用程序的引擎,驱动整个应用程序运行,同时,应用程序也是基于Cpython去实现的,如图14-8所示,Pyodide是CPython到WebAssembly/Emsc…

教程 - 在 Vue3+Ts 中引入 CesiumJS 的最佳实践@2023

1. 本篇适用范围与目的 1.1. 适用范围 严格使用 Vue3 TypeScript 的前端项目&#xff0c;包管理器默认使用 pnpm 构建工具使用 Vite4 使用原生 CesiumJS 依赖做应用开发 客户端渲染&#xff0c;因为我不太熟悉 Vue 的服务端渲染&#xff0c;有本篇的介绍后&#xff0c;熟悉…

06. 管理Docker容器数据

目录 1、前言 2、Docker实现数据管理的方式 2.1、数据卷&#xff08;Data Volumes&#xff09; 2.2、数据卷容器&#xff08;Data Volume Containers&#xff09; 3、简单示例 3.1、数据卷示例 3.2、数据卷容器示例 1、前言 在生产环境中使用 Docker&#xff0c;一方面…

在腾讯云服务器OpenCLoudOS系统中安装jdk

1. 创建jdk安装目录&#xff1a; /app/soft/jdk mkdir -p /app/soft/jdk 2. 将linux版本的jdk安装包上传至/app/soft/jdk 3. 解压jdk安装包 tar -zxvf jdk-8u341-linux-x64.tar.gz 4. 复制jdk安装路径 pwd查看jdk当前路径&#xff0c;得到jdk在系统中的安装路径 5. 配置系…

【力扣周赛】第 356 场周赛(数位DP)

文章目录 Q1&#xff1a;6917. 满足目标工作时长的员工数目&#xff08;简单枚举模拟题&#xff09;Q2&#xff1a;6900. 统计完全子数组的数目&#xff08;双指针滑动窗口&#xff09;Q3&#xff1a;6918. 包含三个字符串的最短字符串Q4&#xff1a;6957. 统计范围内的步进数字…

Vue没有node_modules怎么办

npm install 一下 然后再npm run serve 就可以运行了

AD20. 如何给元器件设计、添加3D模型

Altium Designer学习笔记 - 00.目录​​​​​​​ 零. 前言 本文以HF46F继电器为例展示设计、添加元器件3D模型的流程&#xff0c;其他元器件类似。 一. 操作步骤 从下图可以看到此时继电器还没有添加3D模型&#xff1a; 1. 获取元器件尺寸 这里通过查找元器件的数据手册可以…

el-select 无限下拉滚动加载数据

<template> <div> <el-form ref"saveParameter" :model"saveParameter" inline inline-message style"margin:10px" > <el-form-item label"供应商" prop"lngcustomerid"> <el-select v-model&q…