一文了解数组

640?wx_fmt=jpeg

2019 年第 75 篇文章,总第 99 篇文章

数据结构算法入门系列的第二篇,这次介绍下数组, 数组是一个最基础而且常见的数据结构,几乎每种编程语言都有。

上一篇文章:

数据结构算法入门--一文了解什么是复杂度

今日推荐阅读:

深度学习在推荐系统中的应用


如何实现随机访问

数组的定义:

数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。

这里指出了数组的三个特点:

  1. 线性表(Linear List)

  2. 连续的内存空间

  3. 存储相同类型数据

首先,线性表就是数据排成一条线一样的结构,每个线性表最多只有前后两个方向,线性表结构如下图所示,数组、链表、队列和栈都属于这种结构

640?wx_fmt=png

和线性表对立的就是非线性表,比如二叉树、堆、图等,它们不仅仅是简单的前后关系,如下图所示:

640?wx_fmt=png

接着就是需要连续内存空间和保存相同类型的数据。这两个特点让数组有一个非常好的特性:随机访问。也就是根据下标访问数组的时间复杂度是 O(1) ,但问题就是插入和删除需要 O(n),因为需要进行大量的数据移动操作。

那么数组是如何实现随机访问的操作的呢?

首先,给定一个长度为 10 的 int 类型的数组 a[10] ,计算机会分配一块存储空间,这里假设就是 1000~1039 ,其中内存块的首地址是 base_address=1000,如下图所示

640?wx_fmt=png

计算机给每个内存单元分配一个地址,然后通过地址访问内存中的数据。因此,这里如果需要随机访问数组的某个元素,同样也是根据地址访问,也就是首先需要找到该元素存储所在的地址,寻址公式如下所示:

a[i]_address = base_address + i * data_type_size

data_type_size 表示数组每个元素的大小,这里 int 类型是 4 个字节。

注意,对于数组来说,它支持随机访问,根据下标随机访问的时间复杂度是 O(1) ,但查找时间复杂度并非是 O(1) , 因为即便排好序的数值,通过二分查找,时间复杂度是 O(logn)

低效的插入和删除

数组的插入和删除操作由于其内存数据的连续性问题,这两个操作会非常低效,那么为什么会导致低效,有哪些改进方法呢?

插入操作

假设数组长度是 n ,现在需要将一个数据插入到数组的第 k 位置,如果需要执行这样的操作,需要将第 kn 位置上的元素都按顺序往后移动一位。这个操作的时间复杂度是多少呢?

最好的情况,就是末尾插入元素,这样不需要移动,O(1) 复杂度;最坏的情况,数组开头就插入元素,那么就是 O(n) 的时间复杂度。平均情况时间复杂度则如下所示,每个位置插入元素概率是相同的:

当然,如果数组是有序的,就需要按照上述做法移动元素。如果数组无序呢,一个快速的方法就是仅移动目标位置的元素,即第 k 个位置的元素放到数组末尾,然后插入元素即可,这样时间复杂度就是 O(1)

一个简单的例子如下图所示,数组有 5 个元素:a,b,c,d,e,现在希望在第三个位置插入新元素 x,此时可以直接将 c 放到末尾,即 a[5] = a[2],然后 a[2]=x,即可完成操作。

640?wx_fmt=png

这种特殊的处理技巧,可以在特定场景下(比如数组无序)将插入元素的时间复杂度降到 O(1)

删除操作

和插入数据类似,删除第 k 个位置元素,同样需要将后续的元素往前移动。

  • 最好的情况是删除末尾数据,O(1)

  • 最坏就是删除开头的元素,O(n)

  • 平均情况时间复杂度也是 O(n)

同样在某些特定场景下,并不需要时刻追求数组中数组的连续性,可以将多次删除操作集中在一起进行操作。

如下图所示是一个长度为 10 的数组,存储了 8 个元素,`现在是需要依次删除前三个元素,a,b,c。如果每次删除操作都将所有元素往前移动,那么后续的 5 个元素总共需要移动 3 次,为了避免这个情况,我们可以先记录被删除的数据,每次操作仅仅记录那个位置的元素被删除。当数组没有空间存储数据时,再进行一次真正的删除操作,这样可以避免删除操作导致的数据搬移。

640?wx_fmt=png

这个做法其实就是 Java 中 JVM 标记清除垃圾回收算法的核心思想。

所以,我们对于数据结构和算法的学习,不应该只是死记硬背,而是需要了解它背后的思想和处理技巧,明白为什么需要这种数据结构和这种算法。

数组的越界问题

首先来看一段 C 语言的代码,如下所示:

int main(int argc, char* argv[]){	int i=0;	int arr[3] = {0};	for(; i<=3; i++){	arr[i] = 0;	printf("hello world\n")	}	return 0;	
}

这段代码的问题就是打印结果的时候,因为循环结束条件问题,会无限打印 "hello world",给定的数组长度是 3, 但是循环结束条件是 i<=3 ,而 a[3] 其实就是访问越界了。

但是,在 C 语言中,只要不是访问受限的内存,所有的内存空间都是可以自由访问的。也就是说,a[3] 也是可以访问的,但是会定位到非数组所在的内存上,而这个地址正好是存储变量 i 的内存地址,也就是 a[3]=0 就相当于 i=0 ,最终导致代码的无限循环。

数组越界在 C 语言中是一种未决行为,没有规定这种情况编译器应该如何处理,所以通常会出现各种奇怪的逻辑错误。

不过,其他编程语言并不会将数组越界的工作丢给程序员来做,它们会有做越界的检查。

数组索引从 0 开始的原因

大多数的编程语言中,数组,或者说数据结构,索引都是从 0 开始,而不是从 1 开始。

首先,从数组存储的内存模型上看,索引,或者说“下标”最确切的定义应该是偏移(offset)

前面介绍了数组的寻址公式:

a[i]_address = base_address + i * data_type_size

如果数组是从 1 开始计数,那么寻址公式就会为:

a[i]_address = base_address + (i-1) * data_type_size

对比两个公式,可以知道每次访问数组元素,从 1 开始计数的方式会多一次减法运算,相当于让 CPU 多一次减法指令,但随即访问数组元素应该是非常基础的操作,需要尽可能高效,因此,为了减少一次减法操作,数组采用从 0 开始计数,而非从 1 开始。

其次,主要是 C 语言设计者用 0 开始计数,后续的编程语言,如 Java 等都效仿了 C 语言,这也是方便 C 语言程序员学习其他语言的学习成本。当然,像 Python 还支持负数下标。


参考:

  • 极客时间的数据结构与算法之美课程

欢迎关注我的微信公众号--算法猿的成长,或者扫描下方的二维码,大家一起交流,学习和进步!

640?wx_fmt=png

如果觉得不错,在看、转发就是对小编的一个支持!

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

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

相关文章

Python3.8 了解的差不多了吧,Python3.9 新特性了解一下!

"Python学习开发"&#xff0c;一个值得加星标的公众号。正文共&#xff1a;4946 字 1 图预计阅读时间&#xff1a;13 分钟作者:陈祥安原文有删改:https://docs.python.org/3.9/whatsnew/3.9.html本文将解释 Python 3.9 中的新特性&#xff0c;而不是 3.8。有关完整的…

React工作(1)---export导出

import React, { Component } from react;class List extends Component {constructor(props) {super(props);}render() {return <div>1111</div>;} }export default List;

首发:适合初学者入门人工智能的路线及资料下载

本文为AI入门提供了一个简易的学习路线&#xff0c;并提供了代码和数据集下载。&#xff08;黄海广&#xff09;一、前言AI以及机器学习入门&#xff0c;初学者遇到的问题非常多&#xff0c;但最大的问题就是&#xff1a;资料太多&#xff01;&#xff01;&#xff01;看不完&a…

动态SQL中变量赋值

在动态SQL语句中进行变量的值绑定比较麻烦&#xff0c;这儿做个记录 declare COUNT int,sql nvarchar(max) set sql select COUNT count(id) from tbl_sys_dict where root_key WT_TYPE execute sp_executesql sql, NCOUNT int output, COUNT output print COUNT 转载于:htt…

数据结构算法入门--链表

2019 年第 76 篇文章&#xff0c;总第 100 篇文章 本文大约 3200 字&#xff0c;阅读大约需要 10 分钟 数据结构算法系列&#xff1a; 数据结构算法入门系列第三篇--链表&#xff0c;链表也是非常常见的数据结构&#xff0c;面试过程中也会经常考到相关的题目。 本文首先介绍链…

react学习(3)----不能在该位置用setstate

this.setState({ pageIndex: 1, pageSize: 10, });

后台命名查询sql查某几个字段传到前台

dwr调用查出数据库字段 传给前台显示 前台只能接受list 后台数据放进list传入前台 name-quary中 <!-- 定义sql 通过单位id 查单位名称 --> <sql-query name"getStationName"> <![CDATA[ select STATION_ID,STATION_NAME from MF_STATION wher…

带你少走弯路:强烈推荐的Keras快速入门资料和翻译(可下载)

上次写了TensorFlow和PyTorch的快速入门资料&#xff0c;受到很多好评&#xff0c;读者强烈建议我再出一个keras的快速入门路线&#xff0c;经过翻译和搜索网上资源&#xff0c;我推荐4份入门资料&#xff0c;希望对大家有所帮助。备注&#xff1a;另外两个入门资料很负责任地说…

hadoop遇到的问题及处理

1:杀掉hadoop作业 列出作业 ./hadoop job -list杀掉 ./hadoop job -kill job_id1&#xff1a;某些节点出现running asprocess XXX. Stop it first 这是由于各节点登录用户为root&#xff0c;在启动hadoop前&#xff0c;务必将各节点用户切换至普通用户hadoop下&#xff0c;切换…

Mac 下安装配置 Python 开发环境

图片来源&#xff1a;Unsplash&#xff0c;作者 Markus Spiske 2019 年第 77 篇文章&#xff0c;总第 101 篇文章前言记录下 Mac 电脑的开发环境安装配置&#xff0c;主要包括&#xff1a;安装&使用Homebrew安装使用 git安装 anaconda&#xff0c;配置 python3 环境安装 ju…

react学习(5)----通过设置初始值控制页面render渲染

boothActivityCode: this.props.location.query.code || ,

【Android源代码下载】收集整理android界面UI效果源码

在Android开发中&#xff0c;Android界面UI效果设计一直都是很多童鞋关注的问题&#xff0c;今天给大家分享下大神收集整理的多个android界面UI效果&#xff0c;都是源码&#xff0c;都是干货&#xff0c;贡献给各位网友&#xff01; 话不多说&#xff0c;直接上效果图&#xf…

一文了解类别型特征的编码方法

来源&#xff1a;Unsplash&#xff0c;作者&#xff1a;an Rizzari2019 年第 78 篇文章&#xff0c;总第 102 篇文章目录&#xff1a;问题描述数据准备标签编码自定义二分类one-hot 编码总结问题描述一般特征可以分为两类特征&#xff0c;连续型和离散型特征&#xff0c;而离散…

UNIX网络编程--读书笔记

会集中这段时间写UNIX网络编程这本书的读书笔记&#xff0c;准备读三本&#xff0c;这一系类的文章会不断更新&#xff0c;一直会持续一个月多&#xff0c;每篇的前半部分是书中讲述的内容&#xff0c;每篇文章的后半部分是自己的心得体会&#xff0c;文章中的红色内容是很重要…

react学习(6)----react样式多用内联

<div style{{ textAlign: center }}><Button type"primary" style{{ marginRight: 10px }}>保存</Button><Button>取消</Button><Button type"primary" style{{ marginLeft: 10px }}>发布</Button></div>…

如何用栈实现浏览器的前进和后退?

2019 年第 79 篇文章&#xff0c;总第 103 篇文章数据结构与算法系列的第四篇文章&#xff0c;前三篇文章&#xff1a;前言浏览器的前进和后退功能怎么用栈来实现呢&#xff1f;这里先介绍一下栈的定义和实现&#xff0c;并介绍它的一些常用的应用&#xff0c;最后再简单实现一…

iOS开发Objective-C基础之──多态

Objective-C语言是面向对象的高级编程语言&#xff0c;因此&#xff0c;它具有面向对象编程所具有的一些特性&#xff0c;即&#xff1a;封装性、继承性和多态性。 今天介绍一下Objective-C中的多态性。 一、什么是多态 多态&#xff1a;不同对象以自己的方式响应相同的消息的能…

react学习(7)----react转换值同render

{title: 状态,dataIndex: status,render: (text, row) > {let arr [, 未开始, 进行中, 已结束, 已作废];return <span>{arr[text]}</span>;},},

数据科学家令人惊叹的排序技巧

2019 年第 80 篇文章&#xff0c;总第 104 篇文章本文大约 7800 字&#xff0c;阅读大约需要20分钟原题 | Surprising Sorting Tips for Data Scientists作者 | Jeff Hale原文 | https://towardsdatascience.com/surprising-sorting-tips-for-data-scientists-9c360776d7e译者 …