数据结构的加强甜点-序列1

news2024/11/19 10:46:02

目录

尾递归

问题

介绍

特点

原理

答案

数组栈堆内存分配

前言

分析

再分析

所谓多维数组

程序局部性原理应用


尾递归

问题

  • 在空间复杂度这块,有个O(n)示例如下:
void recur(int n) {
    if (n == 1) return;
    return recur(n - 1);
}
  • 这很明显是个尾递归,未啥没优化成O(1)

介绍

  • 在传统的递归中,典型的模型是首先执行递归调用,然后获取递归调用的返回值并计算结果
  • 以这种方式,在每次递归调用返回之前,你不会得到计算结果
  • 这样做的缺点有二:
  • 效率低,占内存
  • 如果递归链过长,可能会statck overflow
  • 若函数在尾位置调用自身(或是一个尾调用本身的其他函数等等),则称这种情况为尾递归
  • 尾递归也是递归的一种特殊情形
  • 尾递归是一种特殊的尾调用,即在尾部直接调用自身的递归函数

特点

  • 对尾递归的优化也是关注尾调用的主要原因
  • 尾递归在普通尾调用的基础上,多出了2个特征:
  • 在尾部调用的是函数自身 (Self-called);
  • 可通过优化,使得计算仅占用常量栈空间 (Stack Space)

原理

  • 当编译器检测到一个函数调用是尾递归的时候,它就覆盖当前的活动记录而不是在栈中去创建一个新的
  • 编译器可以做到这点,因为递归调用是当前活跃期内最后一条待执行的语句,于是当这个调用返回时栈帧中并没有其他事情可做,因此也就没有保存栈帧的必要了
  • 通过覆盖当前的栈帧而不是在其之上重新添加一个,这样所使用的栈空间就大大缩减了,这使得实际的运行效率会变得更高

答案

  • 这段代码是尾递归,理论上可以将空间复杂度优化至O(1)
  • 不过绝大多数编程语言(例如 Java, Python, C++, Go, C# 等)都不支持自动优化尾递归,因此在这里写O(n)

数组栈堆内存分配

前言

  • 栈内存分配由编译器自动完成,而堆内存由程序员在代码中分配(请注意这里的栈和堆不是数据结构中的栈和堆)
  • 1.栈不灵活,分配的内存大小不可更改;堆相对灵活,可以动态分配内存;
  • 2.栈是一块比较小的内存,容易出现内存不足;堆内存很大,但是由于是动态分配,容易碎片化,管理堆内存的难度更大、成本更高;
  • 3.访问栈比访问堆更快,因为栈内存较小、对缓存友好,堆帧分散在很大的空间内,会出现更多的缓存未命中;

分析

  • 假设刚开始,堆、栈是空的
  • 1---声明数组:
  • int[] array = null;
  • array只是声明而已,会在栈为其开辟一个空间,堆为开辟空间
  • 2---创建数组:
  • array = new int[10];
  • 创建数组,在堆里面开辟空间储存数组,同时栈中的array指向该存储空间

  • 3---给数组赋值:
  • for(int i = 0; i < 10; i++) array[i]=i+1;

再分析

  • int[] a = {2,3,4}; 
  • int[] b = new int[4];
  • b = a;
  • // 定义Person类
    public class Person {                                                  
    	public int age;
    	public double height;
        public void info() {
                System.out.println("年龄:" + age + ",身高:" + height);
        }
    }
  • // 测试类
    public class ArrayTest {           
        public static void main(String[] args) {
    
            // 定义一个students数组变量,其类型是Person[]
            Person[] students = new Person[2];
    
            Person zhang = new Person();
            zhang.age = 10;
            zhang.height = 130;
    
            Person lee = new Person();
            lee.age = 20;
            lee.height = 180;
    
            //将zhang变量赋值给第一个数组元素
            students[0] = zhang;
            //将lee变量赋值给第二个数组元素
            students[1] = lee;
    
            //下面两行代码结果一样,因为lee和student[1]
            //指向的是同一个Person实例
            lee.info();
            students[1].info();
        }
    }
  • Person[] students = new Person[2] 时

  • Person zhang = new Person();
  • zhang.age = 10;
  • zhang.height = 130;
  • Person lee = new Person();
  • lee.age = 20;
  • lee.height = 180; 时

  • 将zhang变量赋值给第一个数组元素
  • students[0] = zhang;
  • 将lee变量赋值给第二个数组元素
  • students[1] = lee; 时

所谓多维数组

  • 先说结论:
  • Java语言里提供了支持多维数组的语法
  • 如果从数组底层的运行机制上来看,没有多维数组!
  • 开始讲解:
  • Java里数组是引用类型,因此数组变量其实是一个引用
  • 什么是引用?
  • 引用=起个别名;并不另外开辟内存单元;但占用内存的同一位置
  • 什么是引用类型?
  • 值类型直接存储其值,而引用类型存储对其值的引用
  • 这个引用指向真实的数组内存,如果数组元素也是引用类型,也就是多维数组
  • 那是不是最终都指向一维数组的内容
  • 举例说明:
  • 定义一个二维数组,当作一维数组遍历
  • 结果为null null null null,
  • 说明二维数组其实就是一维数组里元素的引用类型
  • 即一个长度为2的数组
  • // 定义一个二维数组
    int[][] a;
    // 把a当作一维数组进行初始化,初始化a是一个长度为4的数组
    // a数组的元素又是引用类型
    a = new int[4][];
    // 把a当作一维数组进行遍历,遍历a的每个元素
    for (int i = 0; i < a.length; i++) {
        System.out.println(a[i]);// null null null null
    }
    // 初始化a的第一个元素
    a[0] = new int[2];
    // 访问a数组的第一个元素所指向数组的第二个元素
    a[0][1] = 6;
    // a数组的第一个元素是一个一维数组,遍历这个一维数组
    for (int i = 0; i < a[0].length; i++) {
        System.out.println(a[0][i]);
    }
  • int[][] a;
  • a = new int[4][]; 时
  • a[0] = new int[2];
  • a[0][1] = 6; 时
  • 程序采用动态初始化a[0]数组,
  • 因此系统将为a[0]所引用数组的每个元素默认分配0,
  • 程序显示的将a[0]的第二个元素赋值为6
  • 再举例:
  • int[][] b = new int[3][4];
  • 同时初始化二维数组的两个维数
  • 该代码定义了一个b数组变量,这个数组变量指向一个长度为3的数组
  • 这个数组的元素又是一个数组类型,它们各指向对应长度为4的int[]数组
  • 每个数组元素的值为0

程序局部性原理应用

  • 除了查找性能链表不如数组外
  • 还有一个优势让数组的性能高于链表,即程序局部性原理
  • 这里简介一下,这属于组成原理的内容
  • 我们知道 CPU 运行速度是非常快的,如果 CPU 每次运算都要到内存里去取数据无疑是很耗时的
  • 所以在 CPU 与内存之间往往集成了挺多层级的缓存,这些缓存越接近CPU,速度越快
  • 所以如果能提前把内存中的数据加载到如下图中的 L1, L2, L3 缓存中,那么下一次 CPU 取数的话直接从这些缓存里取即可,能让CPU执行速度加快
  • 那什么情况下内存中的数据会被提前加载到 L1,L2,L3 缓存中呢
  • 答案是当某个元素被用到的时候,那么这个元素地址附近的的元素会被提前加载到缓存中
  • 以整型数组 1,2,3,4为例
  • 当程序用到了数组中的第一个元素(即 1)时
  • 由于 CPU 认为既然 1 被用到了,那么紧邻它的元素 2,3,4 被用到的概率会很大,所以会提前把 2,3,4 加到 L1,L2,L3 缓存中去,这样 CPU 再次执行的时候如果用到 2,3,4,直接从 L1,L2,L3 缓存里取就行了,能提升不少性能

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

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

相关文章

Canal(1):Canal入门

1 什么是 Canal 阿里巴巴 B2B 公司&#xff0c;因为业务的特性&#xff0c;卖家主要集中在国内&#xff0c;买家主要集中在国外&#xff0c;所以衍生出了同步杭州和美国异地机房的需求&#xff0c;从 2010 年开始&#xff0c;阿里系公司开始逐步的尝试基于数据库的日志解析&am…

SpringSecurity之微服务权限解决方案

目录 前置知识点 什么是微服务 微服务的优劣 优点 缺点 微服务本质 微服务认证与授权实现思路 认证预授权的过程 前置知识点 什么是微服务 微服务&#xff08;或称微服务架构&#xff09;是一种云 原 生 架构方法&#xff0c;在单个应用中包含众多松散耦合而且可单独部…

君子生非异也,善假于物也。【借助外力获取能量,主动改善生存环境。】

文章目录 引言I 借助外力1.1 制造很实用工具1.2 火引言 人类懂得借助外力,从被动地适应环境的进化,进入到主动改善生存环境的发展轨道上了。 人之所以为人,是因为我们善于借助外力,而不是先天有多少优势。 I 借助外力 学会制造和使用工具,提高能量获取的效率学会使用火,…

苹果手写笔有必要买吗?性价比电容笔排行榜

众所周知&#xff0c;苹果的正版Pencil的售价过于的高&#xff0c;一般的用户是买不起的。那么&#xff0c;市场上是否会有一款苹果Pencil的平替电容笔&#xff0c;而这两款电容笔在功能上是完全相同的&#xff1f;的确如此。国内的平替电容笔在书写方面上跟苹果Pencil差别不大…

【多线程的应用】顺序打印

【多线程的应用】顺序打印题目注意点&#xff1a;1. 每个线程循环10次&#xff0c;利用锁的wait 和 计数器count调节线程的执行顺序2. count后 lock.notifyAll 唤醒所有线程3. Thread.currentThread().getName()4. 锁中的逻辑是&#xff1a;进入锁中后&#xff0c;如果while不满…

图片怎么转换成pdf格式?这几个方法帮你一键转换

现今电子书籍越来越受到欢迎&#xff0c;其中PDF格式也成为了一种常用的电子书籍格式。无论是工作还是学习&#xff0c;我们都可能会遇到需要将图片转换成PDF格式的情况&#xff0c;例如保存一些资料证明、公文公告、学习资料等。在这篇文章中&#xff0c;我们将为大家介绍三种…

CT前瞻(一):Vant UI入门与使用

文章目录&#x1f4cb;前言&#x1f3af;什么是Vant UI&#x1f3af;快速上手&#x1f3af;组件用法&#x1f4dd;最后&#x1f4cb;前言 最近在项目开发和学习的过程中&#xff0c;涉及到了Vant UI&#xff08;简称Vant&#xff09;的使用&#xff0c;主要还是涉及到了Card卡…

[图神经网络]视觉图神经网络ViG(Vision GNN)--论文阅读

国际惯例&#xff1a; 论文地址https://arxiv.org/pdf/2206.00272.pdfgit地址https://github.com/huawei-noah/Efficient-AI-Backbones/tree/master/vig_pytorch 相较于之前将GNN和CNN结合的图像处理算法&#xff0c;ViG创新的将GNN直接用在了特征提取上。不再需要借用…

Docker快速搭建SkyWalking[ OAP UI[登录] Elasticsearch]

文章目录[前置]&#xff1a;搭建ELasticsearch相关[零]&#xff1a;虚拟机开放SkyingWalking和ES相关端口[一]&#xff1a;拉取SkyWalking-oap和SkyWalking-ui镜像[二]&#xff1a; 运行SkyWalking的oap和ui 容器 2.1 - 运行Skywalking-oap容器 ---- 注意oap运行参数异常 no pr…

早有尔闻 | 数字赋能,提质增效

01 卡奥斯牵头国家重点研发项目 助力中小企业数字化转型升级 3月24日&#xff0c;2022年国家重点研发计划“面向中小企业研发制造资源技术工业互联技术服务平台”项目正式启动。其中&#xff0c;卡奥斯COSMOPlat作为平台承建方牵头课题4“研发制造资源工业互联技术服务平台研…

CDH6.3.2引入debezium-connector-mysql-1.9.7监听mysql事件

1、首先说明一下为啥选用debezium&#xff0c;它能够根据事务的提交顺序向外推送数据&#xff0c;这一点非常重要。再有一个结合kafka集群能够保证高可用&#xff0c;对于熟悉java语言的朋友后面一篇博文会介绍怎样编写插件将事件自定义路由到你想要的主题甚至分区中。 提高按顺…

百度「文心一言」阿里「通义千问」腾讯的AI将会叫什么呢

阿里于昨天2023.4.7下午上线通义千问&#xff0c;与ChatGPT类似&#xff0c;同样是基于语言模型训练的人工智能聊天平台。通义千问的核心功能分为四个大类&#xff1a;撰写短文、职场助理、电影脚本和写封邮件。 通义千问通义千问https://tongyi.aliyun.com/ 首页如下&#xf…

word文件上的电子签章的法律效力如何保证?

你有没有见过这样的word文件“电子签章”&#xff1f; 这种用PS制作的“电子签章”&#xff0c;或者在一些输入公司名称就能在线生成“电子签章”的小网站、小作坊买来的“电子签章”&#xff0c;通通都是没有法律效力的贴图章&#xff01; 使用贴图章的word文件不但没有任何…

VsCode 将源代码管理(Working tree)中的新旧代码上下对比变为左右对比

文章目录一、默认设置二、左右布局变成了上下布局三、解决方法&#xff1a;将上下布局改为左右布局1&#xff1a;找到右上角的更多设置2&#xff1a;点击更多设置后点击【切换到并排视图】3&#xff1a;效果如下&#xff08;还是原来的效果&#xff09;四、左右切换成上下总结一…

Pytorch实现图像风格迁移(一)

图像风格迁移是图像纹理迁移研究的进一步拓展&#xff0c;可以理解为针对一张风格图像和一张内容图像&#xff0c;通过将风格图像的风格添加到内容图像上&#xff0c;从而对内容图像进行进一步创作&#xff0c;获得具有不同风格的目标图像。基于深度学习网络的图像风格迁移主要…

玩转Fastdfs

FastDFS FastDFS是一个开源的轻量级分布式文件系统。它解决了大数据量存储和负载均衡等问题。特别适合以中小文件&#xff08;建议范围&#xff1a;4KB < file_size <500MB&#xff09;为载体的在线服务&#xff0c;如相册网站、视频网站等等 特性 文件不分块存储&am…

fast-lio2代码解析

代码结构很清晰&#xff0c;从最外层看包含两个文件夹&#xff0c;一个是fast-lio,另外一个是加上scan-context的回环检测与位姿图优化。 fast-lio 主要是论文的fast-lio2论文的实现&#xff0c;包括前向处理和ikd-tree的实现 1.先从cmakelist入手看代码结构&#xff1a; #这…

瑞芯微RK3568核心板强在何处?

RK3568核心板产品简介 RK3568核心板是武汉万象奥科基于瑞芯微Rockchip的RK3568设计的一款高性能核心板。该处理器集成了最新的高性能CPU、GPU&#xff0c;并拥有丰富的接口&#xff0c;非常适用于工业自动化控制、人机界面、中小型医疗分析器、电力等多种行业应用。 HD-RK3568-…

【Python】Json读写操作_JsonPath用法详解

【Python】Json读写操作_JsonPath用法详解 文章目录【Python】Json读写操作_JsonPath用法详解1. 介绍2. 代码示例3. 参考1. 介绍 JSONPath是一种信息抽取类库&#xff0c;是从JSON文档中抽取指定信息的工具&#xff0c;提供多种语言实现版本&#xff0c;包括Javascript、Pytho…

【dp】不同的子序列 两个字符串的删除操作 编辑距离

115. 不同的子序列 dp[i][j]&#xff1a;以j-1为结尾的t出现在以i-1为结尾的s子序列的个数 需要开辟m1行&#xff0c;n1列的二维数组 为啥状态方程是&#xff1a; s[i] t[j] 时 dp[i][j] dp[i-1][j-1] dp[i-1][j] s[i] ! t[j] 时 dp[i][j] dp[i-1][j] 先看s[i] t[j] 时…