《恋上数据结构与算法》第1季:算法概述

news2025/8/4 4:32:09

数据结构与算法的学习笔记目录:《恋上数据结构与算法》的学习笔记 目录索引

算法概述

  • 1. 算法和数据结构
    • 1.1 什么是算法
    • 1.2 什么是数据结构
  • 2. 时间复杂度
    • 2.1 如何判断一个算法的好坏呢?
    • 2.2 基本操作执行次数
    • 2.3 大O表示法
  • 3. 空间复杂度
    • 3.1 概念定义
  • 4. 算法优化
  • 5. 总结

1. 算法和数据结构

1.1 什么是算法

算法是用于解决特定问题的一系列的执行步骤(方法)。

比如:计算a和b的之和、计算1+2+3+…+n的和

//计算a和b的之和
public static int plus(int a, int b){
    return a + b;
}

//计算1+2+3...+n的和
public static int sum(int n){
    int result = 0;
    for(int i = 1; i <= n; i++){
        result += i;
    }
    return result;
}

求解以上示例的方式不局限于上面所说,还可以采用其他方法进行编码,不管采用何种方式,它们之间效率是又区别的。因此,使用不同的算法,解决同一个问题,效率可能相差非常大。比如:求第n个斐波那契数(fibonacci number)。

1.2 什么是数据结构

算法的概念大家大致明白了吧,那数据结构又是什么呢?

我的理解是:数据结构是算法的基石。如果把算法比喻成美丽灵动的舞者,那么数据结构就是舞者脚下广阔而坚定的舞台。

数据结构(data structure),是数据的组织、管理和存储格式,其使用目的是为了高效地 访问 修改数据

那你们知道数据结构都有哪些组成方式吗?

  1. 线性结构
    线性结构是最简单的数据结构,包括数组、链表、以及由它们衍生出来的栈、队列、哈希表…

  2. 舒适相对复杂的数据结构,其中比较又代表性的是二叉树,由它衍生出了二叉堆之类的数据结构…

  3. 图是更为复杂的数据结构,因为在图中会呈现出多对多的关联关系…
  4. 其他数据结构
    除了以上所说的,还有像跳表、哈希链表、位图等等,之后都会涉及讲解到的。

有了数据结构这个舞台,算法才可以尽情舞蹈。在解决问题时,不同的算法会选用不同的数据结构。例如排序算法中的堆排序,利用的就是二叉堆这样的一种数据结构;再如缓存淘汰算法LRU(Least Recently Used)利用的就是特殊的数据结构哈希链表。

关于算法在不同数据结构上的操作过程,在后续会进行一一的学习滴。

2. 时间复杂度

2.1 如何判断一个算法的好坏呢?

衡量算法的好坏有很多标准,其中最重要的两大标准是算法的时间复杂度和空间复杂度。

那时间复杂度和空间复杂度究竟是什么呢?

某一天,小灰和小黄同时加入同一家公司,老板给他们布置了一个需求,直接用代码实现即可。一天后,小灰和小黄交付了各自的代码,两个的代码实现的功能差不多。但小黄的代码运行一次要花100ms,占用内存5MB;而小灰的代码运行一次要花100s,占用内存500MB。于是…

在上述场景中,小灰虽然也按照老板的要求实现了功能,但他的代码中存在两个很严重的问题。

  1. 运行时间长
    运行别人的代码只要100ms,而运行小灰的代码则要100s,使用者肯定是无法忍受的。

  2. 占用空间大
    别人的代码只消耗5MB的内存,而小灰的代码却要消耗500MB的内存,则会给使用者造成很多麻烦。

由此可见,运行时间的长短和占用内存空间的大小,是衡量一个程序好坏的重要因素。

时间复杂度(time complexity):估算程序指令的执行次数(执行时间)
空间复杂度(space complexity):估算所需占用的存储空间

但问题来了,如果代码都还没运行,我怎么能预知代码运行所花的时间呢?

由于受影响环境的和输入规模的影响,代码的绝对执行时间是无法预估的。但我们却可以预估代码的基本操作执行次数。

2.2 基本操作执行次数

关于代码的基本操作执行次数,就是说每条语句在操作中执行多少次。下面就举代码例子说明

代码1:T(n) = 1,执行次数是常量

	public static void test1(int n) {
		
		// if语句执行次数:1
		if (n > 10) { 
			System.out.println("n > 10");
		} else if (n > 5) { // 2
			System.out.println("n > 5");
		} else {
			System.out.println("n <= 5"); 
		}
		
		// for语句执行次数:1(int i = 0) + 4(i < 4) + 4(i ++) + 4(System...) = 13
		for (int i = 0; i < 4; i++) {
			System.out.println("test");
		}
	}

代码2:T(n) = 1 + 3n,执行次数是线性的

public static void test2(int n) {

    // 1(int i = 0) + 3n(i < n, i++, System...)
    for (int i = 0; i < n; i++) {
    System.out.println("test");
	}
}

代码3:T(n) = 3n^2 + 3n + 1,执行次数是用多项式计算的

public static void test3(int n) {

    // 1 + 2n + n * (1 + 3n)【内存循环在外层循环n次基础上在计算次数】
    // = 1 + 2n + n + 3n^2
    // = 3n^2 + 3n + 1
    // O(n^2)
    for (int i = 0; i < n; i++) {
    	for (int j = 0; j < n; j++) {
        	System.out.println("test");
    	}
	}
}

代码4:T(n) = 48n + 1,执行次数是线性的

public static void test4(int n) {

    // 1 + 2n + n * (1 + 15 + 15 + 15)
    // = 1 + 2n + 46n
    // = 48n + 1
    for (int i = 0; i < n; i++) {
    	for (int j = 0; j < 15; j++) {
        	System.out.println("test");
    	}
	}
}

代码5:T(n) = log2(n),执行次数是对数

public static void test5(int n) {
    // 8 = 2^3
    // 16 = 2^4

    // 3 = log2(8)
    // 4 = log2(16)
    //log2(n)
    while ((n = n / 2) > 0) {
    	System.out.println("test");
	}
}

在这里插入图片描述

代码6:T(n) = log5(n),执行次数是对数

public static void test6(int n) {
    //25 = 5^2

    // log5(n)
    while ((n = n / 5) > 0) {
    	System.out.println("test");
	}
}

代码7:T(n) = 1 + 3*log2(n) + 2 * nlog2(n),执行次数是对数

public static void test7(int n) {
    // 1 + 2*log2(n) + log2(n) * (1 + 3n)
    //  = 1 + 3*log2(n) + 2 * nlog2(n)
    for (int i = 1; i < n; i = i * 2) {
    	// 1 + 3n
    	for (int j = 0; j < n; j++) {
        	System.out.println("test");
    	}
	}
}

在这里插入图片描述

代码8:T(n) = 1 + 3n,执行次数是线性的

public static void test8(int n) {
    int a = 10;
	int b = 20;
	int c = a + b;
	int[] array = new int[n];
	// 1 + n + n + n
	// = 
	for (int i = 0; i < array.length; i++) {
    	System.out.println(array[i] + c);
	}
}

2.3 大O表示法

有了基本操作执行次数的函数T(n),是否就可以分析和比较代码的运行时间了呢?还是有一定困难的。

例如算法A的执行次数是T(n)=100,算法B的执行次数是T(n)=5n^2,这两个到底谁的运行时间更长一些呢?这就要看n的取值了。

因此,为了解决时间分析的难题,有了渐进时间复杂度( asymptotic time complexity)的概念,其官方定义如下:

若存在函数f(n),使得当n趋近于无穷大时,T(n)/f(n) 的极限值为不等于零的常数,则称f(n) 是 T(n)的同数量级函数。记作 T(n) = O(f(n)),O为算法的渐进时间复杂度,简称为时间复杂度。

因此,渐进时间复杂度用大写O表示,所以也被称为大O表示法

这个定义好晦涩呀,看不明白!

直白来说,时间复杂度就是把程序的相对执行时间函数T(n)简化为一个数量级,这个数量级可以是n、n2、n3等。

如何推导出时间复杂度呢?有如下几个原则:

● 如果运行时间是常数量级,则用常数1表示
● 只保留时间函数中的最高阶项
● 如果最高阶项存在,则省去最高阶项前面的系数

让我们回头看看刚才那些代码。

代码1:
T(n) = 1,只有常数量级,则转化的时间复杂度为:T(n) = O(1)

代码2:
T(n) = 1 + 3n,最高阶项为3n,省去系数3和常量1,则转化的时间复杂度为:T(n) = O(n)

代码3:
T(n) = 3n^2 + 3n + 1,最高阶项为3n^2,省去系数3、3n和常量1,则转化的时间复杂度为:T(n) = O(n^2)

代码3:
T(n) = 1 + 3*log2(n) + 2 * nlog2(n),最高阶项为 2 * nlog2(n),省去系数2和常量1,则转化的时间复杂度为:
T(n) = O(nlog(n))

注意细节:
1. 大O表示法仅仅是一种粗略的分析模型,是一种估算,能帮助我们短时间内了解一个算法的执行效率
2. 对数阶的细节
a. 对数阶一般省略底数
log2n = log29 ∗ log9n
b. 所以 log2n 、log9n 统称为 logn

常见种类

根据从小到大排列,常见的算法时间复杂度主要有:
O(1) < O(log N) < O(N) < O(Nlog N) < O(N^2) < O(2^N) < O(N!) < O(N^N)
在这里插入图片描述

3. 空间复杂度

3.1 概念定义

空间复杂度涉及的空间类型有:

● 输入空间: 存储输入数据所需的空间大小;
● 暂存空间: 算法运行过程中,存储所有中间变量和对象等数据所需的空间大小;
● 输出空间: 算法运行返回时,存储输出数据所需的空间大小;

通常情况下,空间复杂度指在输入数据大小为 N 时,算法运行所使用的「暂存空间」+「输出空间」的总体大小。
在这里插入图片描述

而根据不同来源,算法使用的内存空间分为三类:

● 指令空间:
编译后,程序指令所使用的内存空间。

● 数据空间:
算法中的各项变量使用的空间,包括:声明的常量、变量、动态数组、动态对象等使用的内存空间。

class Node {
    int val;
    Node next;
    Node(int x) { val = x; }
}

void algorithm(int N) {
    int num = N;             // 变量
    int[] nums = new int[N]; // 动态数组
    Node node = new Node(N); // 动态对象
}

● 栈帧空间:
程序调用函数是基于栈实现的,函数在调用期间,占用常量大小的栈帧空间,直至返回后释放。如以下代码所示,在循环中调用函数,每轮调用 test() 返回后,栈帧空间已被释放,因此空间复杂度仍为 O(1)

int test() {
    return 0;
}

void algorithm(int N) {
    for (int i = 0; i < N; i++) {
        test();
    }
}

算法中,栈帧空间的累计常出现于递归调用。如以下代码所示,通过递归调用,会同时存在 NN 个未返回的函数 algorithm() ,此时累计使用 O(N)大小的栈帧空间。

int algorithm(int N) {
    if (N <= 1) return 1;
    return algorithm(N - 1) + 1;
}

4. 算法优化

● 用尽量少的存储空间
● 用尽量少的执行步骤(执行时间)

根据情况,可以
● 空间换时间
● 时间换空间

关于空间复杂度的知识,我们就介绍到这里。时间复杂度和空间复杂度都是学好算法的重要前提,一定要牢牢掌握哦!

5. 总结

  1. 什么是算法?
  • 在计算机领域里,算法是一系列程序指令,用于处理特定的运算和逻辑问题。
  • 衡量算法优劣的主要标准是时间复杂度和空间复杂度。
  1. 什么是数据结构
  • 数据结构是数据的组织、管理和存储格式,其使用目的是为了高效地访问和修改数据。
  • 数据结构包含数组、链表这样的线性数据结构,也包含树、图这样的复杂数据结构。
  1. 什么是时间复杂度
  • 时间复杂度是对一个算法运行时间长短的量度,用大O表示,记作T(n)=O(f(n))
  • 常见的时间复杂度按照从低到高的顺序,包括O(1)、O(logn)、O(n)、O(nlogn)、O(n^2)等。
  1. 什么是空间复杂度
  • 空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度,用大O表示,记作S(n)=O(f(n))
  • 常见的空间复杂度按照从低到高的顺序,包括O(1)、O()、O(n)等。其中递归算法的空间复杂度和递归深度成正比。

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

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

相关文章

MCE | BCL6 小分子也能发挥类 PROTAC 的功能

图示摘要&#xff1a;来自作者 Jonas Koeppel (Ph. D., Department of Medical Oncology, Dana-Farber Cancer Institute) 的 Twitter杂合双功能降解技术 (PROTAC&#xff0c;靶蛋白配体-Linker-E3 连接酶配体组成的“三体”聚合物) 已被用于多种疾病相关靶点的降解剂开发&…

老年患者植入LVAD的挑战:胃肠道出血

EF值降低的心力衰竭随着年龄的增长越来越常见。据研究报道&#xff0c;75岁以上人群的患病率是普通人群的4倍&#xff0c;目前是老年人死亡和住院的最常见原因之一。治疗方案主要包括心脏移植(HT)和长期左心室辅助装置(LVAD)植入&#xff0c;这两种方法已被临床研究证明可降低患…

Flutter高仿微信-第34篇-单聊-小视频

Flutter高仿微信系列共59篇&#xff0c;从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。 详情请查看 效果图&#xff1a; 详情请参考 Flutter高仿微信-第29篇-单聊 &#xff0c; 这里只是提取小视频的部…

Android App开发即时通信中通过SocketIO在客户端与服务端间传输文本和图片的讲解及实战(超详细 附源码)

需要源码和服务端代码请点赞关注收藏后评论区留下QQ~~~ 一、通过SocketIO传输文本消息 虽然HTTP协议能够满足多数常见的接口交互&#xff0c;但是他属于短连接&#xff0c;每次调用完就自动断开连接&#xff0c;并且HTTP协议区分了服务端和客户端&#xff0c;双方的通信过程是…

机器学习知识经验分享之三:基于卷积神经网络的经典目标检测算法

文章目录前言一、一阶段目标检测算法1.YOLO系列算法2.SSD检测算法3. RetinaNet检测算法二、两阶段目标检测算法1.Faster R-CNN检测算法2.Mask R-CNN检测算法3.Cascade R-CNN检测算法总结前言 本系列文章将对机器学习知识进行分享总结。便于大家从理论层面了解人工智能基础原理…

软件被人后台篡改了收款码属于入侵吗?

最近很多做平台的小伙伴&#xff0c;碰到了同样的问题&#xff0c;就是软件程序后台被恶意篡改收款二维码 这个问题出现在平台主身上无疑是雪上加霜&#xff0c;第一时间找到了小蚁君&#xff0c;分析了一下当时的情况&#xff0c;先安装了小蚁的入侵检测系统&#xff0c;显示…

计算机毕业设计之java+ssm协同办公系统

项目介绍 本公司文档协同办公管理系统采用SSM&#xff08;SpringSpringMVCMyBatis&#xff09;框架开发,主要包括系统用户管理模块、用户信息模块、文件信息管理、个人事务管理、资料信息管理、登录模块、和退出模块等多个模块. 本系统主要包含了等系统用户管理、用户信息管理…

webpack5 PWA解决Web App 项目网络离线情况没法访问情况

为什么 开发 Web App 项目&#xff0c;项目一旦处于网络离线情况&#xff0c;就没法访问了。 我们希望给项目提供离线体验。 是什么 渐进式网络应用程序(progressive web application - PWA)&#xff1a;是一种可以提供类似于 native app(原生应用程序) 体验的 Web App 的技术。…

Go语言中操作Redis

Redis介绍 Redis是一个开源的内存数据库&#xff0c;Redis提供了多种不同类型的数据结构&#xff0c;很多业务场景下的问题都可以很自然地映射到这些数据结构上。 除此之外&#xff0c;通过复制、持久化和客户端分片等特性&#xff0c;我们可以很方便地将Redis扩展成一个能够包…

Word控件Spire.Doc 【图像形状】教程(5) 如何在 C# 中将文本环绕在图像周围

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

frp篇---frp-notify + Gotify 实现 FRP 用户上线通知

frp-notify Gotify 实现 FRP 用户上线通知1. 安装frp_notify2. Gotify 配置3. frp-notify 配置启动 frp_notify开机自启动1. 安装frp_notify 一个专注于消息通知的 frp server manager plugin 实现&#xff0c;让你对进入 frps 的连接了如指掌&#xff0c;不再裸奔。 项目链…

SSM之Spring注解式缓存Redis

目录 Sprig整合Redis 导入相关pom依赖 添加对应的的配置文件 IEDA安装lombok插件 引入外部多文件 applicationContext.xml的整合配置文件 redis注解式缓存 Cacheable 测试类注解 Cacheable 的测试代码 CachePut CachePut测试代码 CacheEvict CacheEvict测试代码 Spr…

如何考察候选人 Vue 技术水平?

答对这些问题&#xff0c;检测你是否真正掌握了Vue 请说一下响应式数据的原理 默认 Vue 在初始化数据时&#xff0c;会给 data 中的属性使用 Object.defineProperty 重新定义所有属性&#xff0c;当页面到对应属性时&#xff0c;会进行依赖收集(收集当前组件中的 watcher)如果…

论文阅读【8】Conditional Random Fields: An Introduction

1.概述 1.1 论文相关 这篇论文是介绍一个经典模型&#xff0c;条件随机场&#xff08;CRF&#xff09;。在很多领域中都存在序列标注任务&#xff0c;例如生物信息识别&#xff0c;计算机语言学和语音识别任务&#xff0c;其中自然语言处理中的词性标注任何和命名实体识别任务…

JS 数据结构:链表

单链表 每个节点中只包含一个指针域的链表称为单链表。 头结点—其指针域指向表中第一个结点的指针&#xff08;头结点不是必须的&#xff0c;只是习惯上加上头结点&#xff0c;而头结点的数据域一般记录的是该链表的相关数据&#xff0c;如&#xff1a;链表长度&#xff09;…

Redis-Linux中安装Redis、命令操作Redis

目录 一、Redis简介 NoSQL与SQL的区别 二、Linux上安装redis 上传并解压redis.gz 进入 redis的解压目录&#xff0c;执行命令 make ​编辑 修改redis为守护进程 们测试一下能否远程连接RedisDesktopManager客户端 开放6379端口 授权&#xff0c;允许远程连接 三、redis命…

小程序上新(2022.10.13~11.14)

20221101 【官方公告】境外主体小程序补充信息存储地区通知20221103 小程序基础库 2.27.1 更新 新增 框架 新增 xr-fame 能力&#xff0c;kanata 更新 详情新增 组件 map 组件新增 bindrendersuccess 属性 详情 (官方文档还查不到这个)新增 API 新增 wx.getRendererUserAgen…

tep时隔8个月迎来重大全新升级

tep此次更新&#xff0c;旨在从“工具”升级为“框架”&#xff0c;为此做了大量的代码整洁工作&#xff0c;重新设计了部分功能&#xff0c;项目脚手架也焕然一新。 功能展示 conftest.py 脚手架生成的conftest.py只有一行代码&#xff1a; fixture自动加载等操作都隐藏到了te…

【学习笔记22】JavaScript数组的练习题

笔记首发 一、已知一个排序好的数组 将数字按照原有顺序插入到数组内 var arr [10, 20, 30, 40, 50];var n 11;// 1. 将n插入数组中arr.push(n);// 2. 冒泡排序for (var k 0; k < arr.length - 1; k) {for (var i 0; i < arr.length - 1 - k; i) {if (arr[i] > …

antd——使用a-tree组件实现 检索+自动展开+自定义增删改查功能——技能提升

之前写后台管理系统时&#xff0c;遇到一个下面的需求&#xff0c;下面是最终完成的效果图。 实现的功能有&#xff1a; 1. 下拉 选择不同的类型——就是一个普通的select组件&#xff0c;下面并不做介绍 2. 通过关键字可以进行tree树形结构的筛选&#xff0c;然后将筛选后的…