数据结构与算法01:时间复杂度

news2024/9/21 8:39:04

目录

【复杂度分析】

【降低时间复杂度】

降低时间复杂度的必要性

【每日一练】


不管是使用什么编程语言或者哪种数据库,不管是解决项目中的什么问题,都离不开数据结构与算法。所谓数据结构就是指某一种数据的存储结构,所谓算法就是操作这种数据的方式方法。举个生活中的例子,比如查字典,字典中的所有内容就可以理解为是一种数据结构,而如何找到想要的字就需要算法来处理。比如软件开发中常用的MySQL、Redis以及各类编程语言中都用到了大量的数据结构和算法。熟悉各类数据结构存储和算法能够编写出高效的程序代码。

作为开发人员,一般比较常用的数据结构有:数组、链表、堆、栈、队列、树、跳表、图;常用的算法有:递归、排序、查找、贪心算法、哈希算法、分治算法、深度优先、广度优先、动态规划等。

线性表:如果一组数据结构中的数据排成像一条线一样,那么就是线性表,每个线性表上的数据最多只有前和后两个方向(指针)。数组、链表、队列、栈 都是线性表结构

非线性表:数据之间并不是简单的前后关系,二叉树、堆、图 都是非线性表结构。

【复杂度分析】

复杂度分为“时间复杂度”和“空间复杂度”,主要用来估算某种数据结构或者算法的执行效率,表示的是一个算法执行效率与数据规模增长的变化趋势,一般使用大O来表示。常见的复杂度量级如下(按照数量级递增):

复杂度表示说明
O(1)常数级别
O(log(n))对数级别
O(n)线性级别
O(n * log(n))线程对数级别
O(n^2)、O(n^3)、O(n^k)平方级别、立方级别、k次方级别
O(2^n)

指数级别  

O(n!)

阶乘级别  

复杂度的细节说明:

  • O(1) :是常量级时间复杂度的表示方法,并不是只执行了1行代码;如果某段代码执行了4行,那么它的时间复杂度也是 O(1),而不是 O(4)。只要程序算法中没有循环语句和递归语句,即使有上万行代码,那么时间复杂度也是Ο(1)。只要是顺序结构的代码,时间复杂度基本都是O(1)。
  • O(logn) 和 O(n * logn):对数级别,一般会忽略对数的底数,不管是O(log2n) 还是 O(log3n) 都统一表示为O(log(n))。如果对O(log(n))的循环执行 n 遍,时间复杂度就是 O(n * log(n)) ,归并排序 和 快速排序 的时间复杂度就是 O(n * log(n)),一般简写为 O(nlogn)。一般出现二分查找或者分而治之策略的时候,时间复杂度基本都是O(logn)。
  • O(m+n) 和 O(m*n):这种情况的复杂度由m和n两个数据的规模来决定。
  • 关于循环:只关注循环执行次数最多的一段代码,一般会忽略掉常量、低阶、系数,只需要记录一个最大阶的量级就可以了,比如O(n * 2)和O(n)是一样的。如果是k层for循环,那么时间复杂度就是O(n^k)。
  • 关于加法:总复杂度等于量级最大的那段代码的复杂度,因为当变量n无限大的时候,基本上只需要关注最大量级的代码的计算了,比如O(n^2) + O(n) 也就相当于O(n^2)了。
  • 关于乘法:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积,比如嵌套循环了3次,那么复杂度就需要对3次嵌套的复杂度相乘。
package main

import "fmt"

func test1() {
	// O(1) 常量级别复杂度
	var a = 5
	var b = 3
	var c = 1
	fmt.Println(a + b + c)

	// O(logn) 和 O(n * logn) 对数级别
	var d = 1
	for d <= 10 {
		// 变量d的值从1开始取,每循环一次就乘以2;当大于10时循环结束,变量d的取值是一个等比数列。
		// 所以这段代码的时间复杂度是 O(log2n),注意这个2是下标2,输入法打不出来.
		d = d * 2
	}
	fmt.Println(d)

	// O(m+n) :这种情况的复杂度由m和n两个数据的规模来决定
	fmt.Println(test2(100, 300))
}

func test2(m, n int) int {
	// 无法事先评估 m 和 n 谁的量级大,所以不能利用加法法则省略掉其中一个;
	// 所以这段代码的时间复杂度就是 O(m+n)
	var sum1 = 0
	for i := 1; i < m; i++ {
		sum1 = sum1 + i
	}

	var sum2 = 0
	for j := 1; j < n; j++ {
		sum2 = sum2 + j
	}

	return sum1 + sum2

}

func main() {
	test1()
}

时间复杂度与代码的运算方式有关系,空间复杂度与数据结构的设计有关系。比如需要对一个数组的元素逆序输出,可以有下面两种方式:

// 逆序输出数组的元素,方法1:逐个遍历并逆序赋值
// 时间复杂度是O(n),空间复杂度是O(n)
func reverseArray1(arr [5]int) [5]int {
	var newArr = [5]int{}
	for i := 0; i < len(arr); i++ {
		newArr[len(arr)-i-1] = arr[i]
	}
	return newArr
}

// 逆序输出数组的元素,方法2:前后互相调换
// 时间复杂度是O(n/2),也就是O(n),空间复杂度是O(1)
func reverseArray2(arr [5]int) [5]int {
	var tmp = 0
	for i := 0; i < len(arr)/2; i++ {
		tmp = arr[i]
		arr[i] = arr[len(arr)-i-1]
		arr[len(arr)-i-1] = tmp
	}
	return arr
}

func main() {
	fmt.Println(reverseArray1([5]int{1, 2, 3, 4, 5})) //[5 4 3 2 1]
	fmt.Println(reverseArray2([5]int{1, 2, 3, 4, 5})) //[5 4 3 2 1]
}

上面两种方法输出的结果都是正确的,但是第二种的空间复杂度是常数1,因此效率更高。

【降低时间复杂度】

降低时间复杂度的必要性

实际的生产环境中,用户的访问请求可以看作一个流式数据,假设这个数据流中每个访问的平均时间间隔是t,如果代码无法在 t 时间内处理完单次的访问请求,那么这个系统最终被大量积压的任务给压垮,这就要求开发人员必须通过优化代码来降低时间复杂度。

数据量小的时候,不管怎么写程序,运行的结果差别都不会太大;但是如果数据量特别大的情况下,不同的时间复杂度运行的结果可能千差万别。假设某个计算任务需要处理10万条数据,使用不同的时间复杂度的结果如下:

  • 如果是O(n^2)的时间复杂度,那么计算的次数就是 10万*10万 = 100亿次;
  • 如果是O(n)的时间复杂度,那么计算的次数就是10万次;
  • 如果是O(logn)的时间复杂度下,那么计算的次数就是17次左右(log 100000 = 16.61,这里的对数以2为底去估计)。

降低时间复杂度的方法:

(1)空间换时间:假设一段程序在比较低配置的计算机上运行可能需要很长时间,那么就可以花钱购买高配置或者更多的服务器来缩短运行时间,也就是俗话说的“堆机器”。这样的操作降低了时间复杂度,但增加了空间复杂度。但实际上空间(云服务器)是比较廉价的,而时间是很宝贵的,你总不能让用户打开一个页面等待好几分钟吧?

(2)通过程序算法降低时间复杂度,常用的算法有:递归、二分法、排序算法、动态规划等等。

程序优化的核心思路:

  • 暴力解法:在没有任何时间、空间约束下,完成代码任务的开发;
  • 无效操作处理:将代码中的无效计算、无效存储剔除,降低时间或空间复杂度;
  • 时空转换:设计合理数据结构,完成时间复杂度向空间复杂度的转移;

【每日一练】

力扣:两数之和(https://leetcode.cn/problems/two-sum/)

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例:输入:nums = [2,7,11,15], target = 9,输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

思路1:暴力解法,双重循环遍历。时间复杂度: O(n^2),空间复杂度: O(1)

func twoSum1(nums []int, target int) []int {
	// 第一轮遍历
	for i := 0; i < len(nums); i++ {
		// 第二轮遍历不能重复计算了
		for j := i + 1; j < len(nums); j++ {
			if nums[i]+nums[j] == target {
				// 注意 leetcode 中要求返回的是索引位置
				return []int{i, j}
			}
		}
	}
	return []int{}
}

func main() {
	fmt.Println(twoSum1([]int{2, 7, 11, 15}, 22)) //[1 3]
}

思路2:空间换时间,使用map(键值对)存储。时间复杂度: O(n),空间复杂度: O(n)

func twoSum2(nums []int, target int) []int {
	find := map[int]int{}
	for j, num := range nums {
		if i, ok := find[target-num]; ok {
			return []int{i, j}
		}
		// 每一轮都存下当前num和对应的index到map中
		find[num] = j
	}
	return []int{}
}

func main() {
	fmt.Println(twoSum2([]int{2, 7, 11, 15}, 22)) //[1 3]
}

源代码:https://gitee.com/rxbook/go-algo-demo/blob/master/algo01/demo1.go

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

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

相关文章

【ClickHouse】

文章目录 一、表引擎1、表引擎的作用2、TinyLog3、Memory4、MergeTree二、数据库引擎1、作用--跨种类交换数据2、示例 三、MergeTree引擎1、简单使用2、分区partition by3、主键primary key4、order by&#xff08;必填&#xff09; 一、表引擎 1、表引擎的作用 CK表引擎决定…

U盘无法格式化?试试这几种快速有效的方法!

我们在通过Windows文件资源管理器、磁盘管理或Diskpart命令格式化U盘过程中&#xff0c;会遇到Windows无法格式化U盘。导致出现该问题的原因可能有以下几种&#xff1a; U盘被病毒和恶意软件感染&#xff1b; U盘有坏道&#xff1b; U盘已经物理损坏&#xff1b; 1.消除可能…

数据类型的陷进,从表象看本质!

哪些值转为布尔值为false 1、undefined&#xff08;未定义&#xff0c;找不到值时出现&#xff09; 2、null&#xff08;代表空值&#xff09; 3、false&#xff08;布尔值的false&#xff0c;字符串"false"布尔值为true&#xff09; 4、0&#xff08;数字0&…

LeetCode每日一题之209长度最小的子数组

文章目录 问题描述方法一&#xff1a;暴力求解方法二&#xff1a;滑动窗口 问题描述 方法一&#xff1a;暴力求解 暴力求解法&#xff1a;时间复杂度O(n^2)&#xff0c;空间复杂度O(1)。 暴力求解法的思想&#xff1a;每一次遍历数组&#xff0c;然后更新result的值&#xff0…

【Linux基本指令(3)】几十条指令快速入手Linux/到底什么是日志?/指令的运行原理到底是什么?

本文思维导图&#xff1a; 文章目录 13.head指令14.tail指令15.时间相关指令到底什么是日志&#xff1f;时间戳 16.cal指令17.find指令&#xff08;非常重要&#xff09;18.grep指令19.zip/unzip指令20.tar指令&#xff08;非常重要&#xff09;21.bc指令22.uname指令23.几个重…

香橙派4和树莓派4B构建K8S集群实践之六:虚拟主机

1. 说明 根据之前设置好的基础&#xff0c;我打算设置两种不同的虚拟主机运行在这个K8s集群上面&#xff0c;一个是LNMP体系的WebApp&#xff0c;一个是Java SpringBoot体系的WebApp。 1.1 设置表格 - wwwroot 是之前做pvc定义的文件目录&#xff0c;形如&#xff1a;/data0…

外包没有前途的,已经被替换了....

我25岁的时候&#xff0c;外包测试&#xff0c;薪资13.5k&#xff0c;人在深圳。 内卷什么的就不说了&#xff0c;而且人在外包那些高级精英年薪大几十的咱也接触不到&#xff0c;就说说外包吧。假设以我为界限&#xff0c;25岁一线城市13.5k&#xff0c;那22-24大部分情况下是…

花指令问题

前言 想起之前打题的时候经常会遇到一些关乎花指令的问题&#xff0c;但是没有系统地总结归纳花指令去除的姿势&#xff0c;浅浅开一个坑慢慢来写 题1&#xff1a;简单jmp 可以骗过dbg&#xff0c;但是放在ida中就很容易看出来&#xff0c;无效跳转 题目来源&#xff1a;[HD…

七牛云图床设置

文章目录 七牛云图床设置下面是用picgo配置图床SSL证书申请https网站显示http图片解决方案 原文链接图床设置&#xff0c;免费SSL证书申请&#xff0c;https网站显示http链接的图片 七牛云图床设置 登录七牛云官网并进行个人注册&#xff0c;然后找到对象存储 点击空间管理&a…

一种适用于大量租户大量角色权限系统设计

前言 权限管理是每个系统不可缺少的一部分&#xff0c;大部分开发者应该都设计过权限管理系统&#xff0c;很多开发者学习的第一个项目可能就是权限管理系统。但是常见的权限设计在租户量非常大、角色数量非常多时会存在角色权限表数据量指数增长的情况&#xff0c;本文介绍一…

c++代码手撕红黑树

企业里永远是技术驱动理论发展 比起理解红黑树的原理&#xff0c;更重要的是理解红黑树的应用场景&#xff0c;因为某些应用场景的需要&#xff0c;红黑树才会应运而生。 红黑树的特点&#xff1a; 插入&#xff0c;删除&#xff0c;查找都是O(logn)的复杂度。 红黑树的应用…

【lager】日志系统2:测试程序调试

design 官方设计文档 design cmakelist增加plog构建 cmake_minimum_required(VERSION 3.1.3 FATAL_ERROR) project(Lager)set(CMAKE_CXX_STANDARD 11) set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)find_package(

课时八——进程同步(二)

1、信号量 信号量机制是一种功能较强的机制&#xff0c;可用来解决互斥和同步问题&#xff0c;它只能被两个标准的原语wait(S)&#xff08;P操作&#xff09;和signal(S)&#xff08;V操作 &#xff09;访问。 注意&#xff1a;原语是一种特殊的程序段&#xff0c;其执行只能一…

EV 电动汽车远程监控系统

EV 电动汽车远程监控系统 远程监控系统简介 目录 EV 电动汽车远程监控系统 1、远程监控系统是什么&#xff1f; 2、远程监控系统包含什么&#xff1f; 2.1车载终端 2.2、数据服务器 ​​​​​​​ 2.3、监控页面 3、远程监控系统有什么用&#xff1f; 4、车载终端…

中性市场观下,贝壳驶入长期价值之海

&#xff08;图片来源于网络&#xff0c;侵删&#xff09; 文 | 螳螂观察 作者 | 易不二 2023年一季度&#xff0c;房地产市场实现了久违的回暖。 国家统计局公布的数据显示&#xff0c;2023年1-4月&#xff0c;全国商品房销售额39750亿元&#xff0c;增长8.8%&#xff0c;…

Atlassian数据迁移攻略:迁移前必备须知

到2024年2月&#xff0c;Atlassian将终止对Server产品及插件的所有支持。是时候制定您的迁移计划了——Atlassian为您提供两种迁移选择&#xff0c;一是本地部署的数据中心版本&#xff0c;中国用户25人以上即可使用&#xff0c;二是云版。作为Atlassian全球白金合作伙伴&#…

es Elasticsearch 六 java api spirngboot 集成es

目录 Java restApi Springboot 集成es 新增-同步 新增-异步 增删改查流程 _bulk 批量操作 Java restApi Springboot 集成es 新增-同步 Testpublic void te2() throws IOException {System.out.println(1);IndexRequest ir new IndexRequest("test");ir.id(&qu…

STM8 模拟iic接口调试温湿度传感器SHT3x驱动

背景 项目实际使用SHT3x进行温湿度测量&#xff0c;主控芯片采用STM8S003F3P6&#xff0c;并且使用模拟IIC接口的硬件连接。 原理图 如下图所示&#xff0c;使用STM8S003F3P6管脚PB4/PB5进行SHT3x数据接口 SHT3x-DIS是Sensirion新一代的温湿度传感器&#xff0c;精度为2%RH和…

LCD1602介绍和各报错经验总结

LCD &#xff08; Liquid Crystal Display 的简称&#xff09;液晶显示器。能够同时显示16x2&#xff0c;32个字符&#xff0c;是一种专门用来显示字母、数字、符号等的点阵型液晶模块。 LCD1602液晶显示器是广泛使用的一种字符型液晶显示模块。它是由字符型液晶显示屏&#x…

【十字绣】传统手艺-微信小程序开发流程详解

还记得小时候看过母亲的十字绣吗&#xff0c;易学易懂&#xff0c;就是用专用的绣线和十字格布&#xff0c;通过平面坐标计找出位置&#xff0c;对照专用的图案进行刺绣&#xff0c;可作出心中所想的画&#xff0c;奈何所需材料成本不小&#xff0c;这里用小程序简单模拟十字绣…