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

2023-05-16

目录

尾递归

问题

介绍

特点

原理

答案

数组栈堆内存分配

前言

分析

再分析

所谓多维数组

程序局部性原理应用


尾递归

问题

  • 在空间复杂度这块,有个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 缓存里取就行了,能提升不少性能
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构的加强甜点-序列1 的相关文章

  • TCP协议

    文章目录 1 保证可靠性机制1 1 确认应答机制1 1 1确认应答机制概念1 1 2常规确认应答的工作方式1 1 3报文按序到达1 1 4 如何确认历史数据被收到1 1 5 16位序号和16确认序号 xff08 字段讲解 xff09 tcp
  • 1 对数器,二分查找,

    文章目录 对数器二分查找 1 有序序列二分查找 2 在一个有序数组中 xff0c 找 lt 61 某个数最右侧的位置 3 在一个有序数组中 xff0c 找 gt 61 某个数最左侧的位置 4 无序序列二分查找 xff0c 求局部最小值 对数
  • 2 异或位运算大厂必刷题

    文章目录 如何不用额外变量交换两个数一个数组中有一种数出现了奇数次 xff0c 其他数都出现了偶数次 xff0c 怎么找到并打印这种数怎么把一个int类型的数 xff0c 提取出最右侧的1来怎么把一个int类型的数 获取位数为1的数量一个数
  • 链表,栈,队列,递归行为,哈希表,有序表

    文章目录 链表1 单链表 双链表的反转2 删除链表中指定的值 队列1 数组循环队列的实现2 双向链表实现双端队列 栈1 用数组实现栈 栈和队列的面试题1 实现最小栈2 两个栈实现一个队列3 两个队列实现一个栈4 用栈实现图的广度优先遍历5
  • 搭建Zabbix6.0版本

    Zabbix简介 Zabbix是一个企业级的开源分布式监控解决方案 xff0c 由C语言编写而成的底层架构 xff08 server端和agent端 xff09 xff0c 由一个国外的团队持续维护更新 xff0c 软件可以自由下载使用 x
  • Linux--网络服务器配置步骤详情【1】

    目录 一 配置ip地址 二 配置yum服务器 三 配置安装nfs服务器 1 第一台机 xff1a 2 第二台机 xff1a 四 安装配置samba服务器 五 安装配置DHCP 一 配置ip地址 root 64 wenjian vi etc
  • vscode提取拓展时出错。XHR failed

    vscode提取拓展时出错 XHR failed huas weew12的博客 CSDN博客 提取扩展时出错 转载 这这人家的步骤操作 果然就好了
  • python天气语音播报

    今天的小项目是一个天气播报 xff0c 项目效果是点击运行就读出今天的天气 那么我们可以分两步走 xff0c 第一个 xff1a 先爬取到今天的天天气内容 xff0c 第二步 xff1a 电脑读出今天的天气内容 想要电脑读出内容 xff0c
  • Linux配置SSH远程登录管理

    目录 一 SSH协议 1 SSH简介 2 SSH的优点 3 SSH远程控制软件及服务 二 SSH远程管理配置 1 配置OpenSSH服务端 2 使用SSH客户端软件 xff08 1 xff09 SSH远程登录 xff08 2 xff09 s
  • Linux系统防火墙firewalld

    目录 一 firewalld概述 二 firewalld和iptables的关系 三 firewalld区域的概念 四 firewalld数据处理流程 五 firewalld检查数据包源地址的规则 六 firewalld防火墙的配置种类 1
  • ubuntu18.04忘记密码后,如何重置密码的方法

    ubuntu18 04安装在VMware虚拟上 ubuntu18 04忘记密码后 xff0c 如何重置密码 xff1f 重启系统后 xff0c 当跳出如下图所示画面时 xff0c 按住Shift键不放 xff0c 等待 2 但出现如下图所示
  • Cloudflare 小记

    url xff1a https cn airbusan com content individual 五秒盾打开后一般会出现这个页面 xff0c 然后让你点击确认你是不是真人 xff0c 点击成功后会跳往所访问的url页面 有时候不会出现这
  • 2021.11.17 指针引用数组(指针+1,指针-1以及书写格式)

    例如 xff1a int arr 10 61 1 2 3 4 5 6 7 8 9 10 int p 61 arr int q 61 amp arr 0 1 p 和 q 表示的都是一样的 xff0c 表示的都是数组首元素的地址 xff0c 只
  • 超详细的vscode 配置FTP,并本地编辑

    废话少说 xff0c 直接上步骤 xff1a 搜索sftp插件并安装 xff1b 安装成功之后 ctrl 43 shift 43 p 搜索sftp config设置内容 没有的需要自己加 xff0c 有的可以不用加 xff1b 34 nam
  • Python求1+2+3+...+100的值,计算平方根的两个代码程序

    目录 前言 一 求1 43 2 43 3 43 43 100的值 1 实现的功能 2 代码程序 3 运行截图 二 计算平方根 1 实现的功能 2 代码程序 3 运行截图 前言 1 因多重原因 xff0c 本博文由两个程序代码部分组成 xff
  • Python求1+2+3+...+100的值,计算自然数的立方和的两个程序代码

    目录 前言 一 求1 43 2 43 3 43 43 100的值 1 实现的功能 2 代码程序 3 运行截图 二 计算自然数的立方和的 1 实现的功能 2 代码程序 3 运行截图 前言 1 因多重原因 xff0c 本博文由两个程序代码部分组
  • Go基本数据类型与string类型互转

    一 基本数据类型转string类型 方法一 xff1a fmt Sprintf 34 参数 34 表达式 1 官方解释 xff1a Sprintf根据format参数生成格式化的字符串并返回该字符串 func Sprintf format
  • linux下如何设置开机自启(这里以seata服务为例)

    1 编写启动脚本 xff0c 大部分都是相同的 xff0c 但是有些程序可能略要修改 Unit Description 61 Seata Server After 61 network target Service User 61 root
  • MIUI12.5系统精简列表更新版200多个包,ADB卸载

    系统MIUI12 5 6 xff0c 无ROOT无面具没破解 xff0c 仅使用ADB工具箱 设备卸载后经过重启测试是否卡米 xff0c 这些都不会卡米 另外要说 xff0c 有些卸载不会卡米 xff0c 但是功能会失效 xff0c 比如

随机推荐

  • kali安装卡在simple-cdd不动?在右下角,有个小小的符号,找到网络适配器,然后断掉,很快就安装好了。

  • 两种方式为button元素注册点击事件,this指向

    两种方式 xff1b 第一种指向button xff0c 第二种 指向window
  • 强化学习实战——Q learning 实现倒立摆

    倒立摆参数以及数学模型 首先是写一个倒立摆的AGENT模型 pendulum env py import numpy as np import matplotlib pyplot as plt import matplotlib impor
  • dependencies.dependency

    依赖包有两个 xff0c 根据最下一行的提示找到项目的pom xml文件找到依赖
  • Vue3---语法初探

    目录 hello world 实现简易计时显示 反转字符串 显示隐藏 了解循环 了解双向绑定实现简易记事 设置鼠标悬停的文本 组件概念初探 xff0c 进行组件代码拆分 hello world 最原始形态 xff0c 找到 id 为 roo
  • MySQL实战解析底层---普通索引和唯一索引,应该怎么选择

    目录 前言 查询过程 更新过程 change buffer 的使用场景 索引选择和实践 change buffer 和 redo log 前言 在不同的业务场景下 xff0c 应该选择普通索引 xff0c 还是唯一索引 xff1f 假设你在
  • 准备离开:致消散的梦想

    学到现在基本都是悲剧以前的队友现在大多放弃了初心以前的好学长现在摆烂和失败开学时的场景再也见不到了大一开学开启OJ xff0c 那是一个永远绚丽的夜晚不管是学长还是同学 xff0c 都在那时期待未来 xff0c 欲力竭以圆其说而不是现在的颓
  • MySQL实战解析底层---MySQL为什么有时候会选错索引

    目录 前言 优化器的逻辑 索引选择异常和处理 前言 在 MySQL 中一张表其实是可以支持多个索引的但是你写 SQL 语句的时候 xff0c 并没有主动指定使用哪个索引也就是说 xff0c 使用哪个索引是由 MySQL 来确定的不知道你有没
  • 二叉搜索树

    目录 定义简介 查找结点 插入结点 删除结点 排序 二叉搜索树的效率 二叉搜索树的退化 二叉搜索树常见应用 定义简介 二叉搜索树 Binary Search Tree 满足以下条件 xff1a 1 对于根结点 xff0c 左子树中所有结点的
  • AVL 树

    目录 介绍 结点高度 结点平衡因子 AVL 树旋转 右旋 左旋 先左后右 先右后左 旋转的选择 插入结点 删除结点 查找结点 AVL 树典型应用 介绍 在进行多次插入与删除操作后 xff0c 二叉搜索树可能会退化为链表此时所有操作的时间复杂
  • 红黑树(更高级的二叉查找树)

    目录 介绍及性质 红黑树的基本定义 黑高度 时间复杂度 接近于 平衡 操作 红黑树的旋转 红黑树中插入新结点 红黑树中删除结点 红黑树与AVL树的区别 介绍及性质 红黑树 xff08 R B TREE xff0c 全称 xff1a Red
  • MySQL实战解析底层---怎么给字符串字段加索引

    目录 所谓前缀索引 前缀索引对覆盖索引的影响 其他方式 所谓前缀索引 现在 xff0c 几乎所有的系统都支持邮箱登录 xff0c 如何在邮箱这样的字段上建立合理的索引 xff0c 是今天要讨论的问题假设 xff0c 你现在维护一个支持邮箱登
  • Spring Security --- 3.5.7版本升级

    目录 WebSecurityConfigurerAdapter 被弃用 configure WebSecurity web 已经弃用 configure AuthenticationManagerBuilder auth 已经弃用 Spri
  • 双系统下,ubuntu20.04循环登录问题解决记录

    什么是循环登录 xff1a 开机登录页面 xff0c 输入密码后 xff0c 未提示密码错误 xff0c 黑屏一秒继续出现登录页面 xff0c 死循环 由于不能进桌面系统 xff0c 我们只能尝试在终端解决问题了 ctrl 43 alt 4
  • Spring Security --- 快速入门

    概念 Spring Security是一个功能强大且高度可定制的 xff0c 主要负责为Java程序提供声明式的 身份验证和访问控制 的安全框架Spring Security的底层主要是 基于 Spring AOP 和 Servlet 过滤
  • Spring Security --- 基于内存模型创建用户角色

    授权实现方式 基于内存模型实现授权基于默认数据库模型实现授权基于自定义数据库模型实现授权 基于内存模型创建用户角色 在Spring Security4 x版本中 xff0c 登陆的用户有一个默认的ROLE USER角色但是在Spring S
  • Spring Security --- authorizeRequests配置

    目录 自定义配置类之访问权限 匹配顺序规则 访问控制包含 访问控制url匹配 访问控制方法 角色 权限判断 使用注解进行角色权限控制 自定义配置类之访问权限 http authorizeRequests 主要是对url进行访问权限控制通过这
  • 手写Spring框架-前奏-注解与自定义注解

    目录 注解 介绍 功能 分类 注解处理器类库 自定义注解 常用元注解 自定义 注解 介绍 提供一种为程序元素设置元数据的方法用来将任何的信息或元数据 xff08 metadata xff09 与程序元素 xff08 类 方法 成员变量等 x
  • 手写Spring框架-前奏-反射获取Annotation

    目录 所谓反射 反射机制的作用 反射依赖reflect和Class 反射依赖的Class Class类的特点 获取Class对象的三种方式 获取类的构造方法并使用 获取类的成员变量并使用 获取类的成员方法并使用 问题引入 解析类的注解 解析
  • 数据结构的加强甜点-序列1

    目录 尾递归 问题 介绍 特点 原理 答案 数组栈堆内存分配 前言 分析 再分析 所谓多维数组 程序局部性原理应用 尾递归 问题 在空间复杂度这块 xff0c 有个O n 示例如下 xff1a void recur int n if n 6