Learning algorithem the hard way array (part 2)
数组(Array)是一种线性表数据结构。它用一组连续的内存空间来存储一组具有相同数据类型的数据。
上述定义当中有有几个较为关键的概念:
线性表 (Linear List)
线性表是指数据排成一个线型的结构。每个线性表上的数据最多只有前后两个方向。
除了数组之外,链表、队列、栈也是线性表结构。
而与其对应的概念是非线性表,例如二叉树、图、堆等。
连续的内存空间和相同的数据类型
数组的随机访问(根据索引下标访问)时间复杂度为 O(1) 。其随机访问的实现原理是:
a[i]_address = base_address + i * data_type_size
为什么大部分编程语言的数组下标是从 0 开始设计,而不是从 1 开始?
我们来思考另外一个问题,为什么大部分编程语言的数组索引下标是从 0 开始,而不是从 1 开始?如果从 1 开始的话,上述的公式必须转化为:
a[i]_address = base_address + ((i - 1) * data_type_size)
对比从 0 开始的方案,需要多做一次减法运算。因此大部分编程语言都选择从 0 开始作为数组的初始下标。
低效的插入和删除操作
插入操作
假设数组的长度为 n ,我们需要将新元素 e 插入到数组中的第 k 个位置,那我们需要将后续的 n - k 个元素顺序地往后进行移动,因此在这种场景下插入操作的时间复杂度为 O(n) 。
但是有一种特殊场景下:数组仅仅作为一种容器去存储数据,而不用关心数组中存储元素的顺序,能够将数组插入操作的算法复杂度提升为 O(1) 。
举个例子,假设数据 a[10] 中存储了 5 个元素: a、b、c、d、e 。我们将元素 x 插入到第三个位置,只需要将原有的第三个元素 c 移动到最后,再将第三个元素替换为 x ,具体的实现代码如下:
a[5] = c; a[2] = x;
删除操作
与插入操作类似,如果我们要删除第 k 个位置的数据,为了内存的连续性,也需要搬移数据,因此数组的删除操作时间复杂度为 O(n) 。
有一种数组删除操作的优化方案是,不需要每一次删除操作时都移动数据,而是先将被删除的索引位置记录下来,等到数组容量不足时,再一次性地从内存当中移除数据,并移动剩余元素的位置。其核心思想与 JVM 的标记清除内存回收算法非常相似。
数组与容器类型的对比
许多编程语言中都提供了高级的容器类型,例如 Java 的 ArrayList , C++ 的 Vector 等,数组与这些高级容器类型相比,有哪些适合的应用场景了?
适合使用容器类型的场景
高级容器类型支持动态扩容,适合存储元素的大小无法事先确定的场景。例如 ArrayList 会在存储空间不足时,自动扩容 1.5 倍。需要注意的是,我们应该养成设置 ArrayList 初始容量的习惯,因为这样能够节省掉很多次内存分配空间和数据搬移的操作。
//假设从 db 当中获取用户的数据为 10000条 //指定初始容量为 10000 会比不指定容量获得更好的性能 ArrayList<User> list = new ArrayList<User>(10000); for (int i = 0; i < db.getUserList(); i++) { list.add(db[i]); }
适合使用数组的场景
ArrayList 无法存储基本类型:int 、long 等,只能存储包装类型: Integer 、Long ,而 Boxing 和 UnBoxing 会有一定的性能损耗,如果特别关注性能,就可以使用数组。
Java implemention
初步实现了类似于 ArrayList 的代码。ref here
JDK 1.8 ArrayList 的实现代码。ref here
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Java描述设计模式(14):解释器模式
本文源码:GitHub·点这里 || GitEE·点这里 一、解释器模式 1、基础概念 解释器模式是对象的行为模式。给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的表达式。 2、模式图解 3、核心角色 (1)、抽象表达式 Express:声明具体表达式角色需要实现的抽象接口,该接口主要提供一个interpret()方法,称做解释操作。 (2)、终结符表达式 TerminalExpress:实现抽象表达式角色接口,主要是一个interpret()方法;每个终结符都有一个具体终结表达式与之相对应。比如解析c=a+b,a和b是终结符,解析a和b的解释器就是终结符表达式。 (3)、非终结符表达式 NotTerminalExpress:每一条规则都需要一个具体的非终结符表达式用来衔接,一般是指运算符或者逻辑判断,比如c=a+b,“+"就是非终结符,解析“+”的解释器就是一个非终结符表达式。 (4)、环境容器 DataMap:一般是用来存放各个终结符所对应的具体值,比如c=a+b转换为c=1+2。这些信息需要一个存放环境。 4...
- 下一篇
高级渗透测试对公司网站漏洞检测详情
天气逐渐变凉,但渗透测试的热情温度感觉不到凉,因为有我们的存在公开分享渗透实战经验过程,才会让这个秋冬变得不再冷,近期有反映在各个环境下的目录解析漏洞的检测方法,那么本节由我们高级渗透架构师来详细的讲解平常用到的web环境检测点和网站漏洞防护办法。 3.14.1. IIS 3.14.1.1. IIS 6.0 后缀解析 /xx.asp;.jpg目录解析 /xx.asp/xx.jpg (xx.asp目录下任意解析)默认解析 xx.asa xx.cer xx.cdxPROPFIND 栈溢出漏洞PUT漏洞 WebDAV任意文件上传3.14.1.2. IIS 7.0-7.5 / Nginx <= 0.8.37 在Fast-CGI开启状态下,在文件路径后加上 /xx.php ,则 xx.jpg/xx.php 会被解析为php文件 3.14.1.3. 其他 在支持NTFS 8.3文件格式时,可利用短文件名猜解目录文件 3.14.2. Nginx 3.14.2.1. Fast-CGI关闭 在Fast-CGI关闭的情况下, Nginx 仍然存在解析漏洞:在文件路径(xx.jpg)后面加上 %00....
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2全家桶,快速入门学习开发网站教程