一文了解数组

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。有关完整的…

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

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

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

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

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

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

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

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

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;而离散…

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

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

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

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译者 …

几个有趣的python技巧

2019 年第 82 篇文章&#xff0c;总第 106 篇文章标题 | python-is-cool作者 | chiphuyen原文 | https://github.com/chiphuyen/python-is-cool译者 | kbsc13("算法猿的成长"公众号作者)声明 | 翻译是出于交流学习的目的&#xff0c;欢迎转载&#xff0c;但请保留本文…

20191215周学习总结

最近会打算每周总结一下学习的内容&#xff0c;主要内容可能是看过的书的一些学习笔记、论文阅读、学习的知识点以及推荐一些文章。这周的学习包括&#xff1a;推荐系统的知识点整理机器学习的技巧学习linux下两台机器的ssh免密登陆方式书籍阅读效率方法推荐系统因为工作方向的…

玩转12306之系统登录

【申明&#xff1a;本文所涉及的技术和分析的目的都是为了学习和交流&#xff0c;任何人使用文中所提的技术或成果做出的违法事情与我无关&#xff0c;大家购买火车票还是去12306官网上去购买。】 从今天起&#xff0c;我开始分析12306网站的Http请求&#xff0c;以及编写一个客…

Nginx快速搭建和基本使用

2019年第 83 篇文章&#xff0c;总第 107 篇文章最近在工作中项目需要上线&#xff0c;所以也了解到关于一些部署上线的知识内容&#xff0c;Nginx 就是其中一个知识点&#xff0c;主要是可以用它来进行负载均衡&#xff0c;本文的目录如下&#xff1a;简介安装配置基本使用简介…

第二期周总结

第二期的周总结&#xff0c;这次学习的内容可能没有上次那么广泛&#xff0c;主要是因为这周我负责的模块需要测试并进行上线&#xff0c;所以主要学习了解的就是工程开发方面的内容&#xff0c;准确说是部署上线的内容&#xff0c;所以本周主要简单总结这次上线过程的一些内容…

AI知识点(1)--激活函数

2019年第 84 篇文章&#xff0c;总第 108 篇文章本文大约 5000 字&#xff0c;阅读大约需要 15 分钟AI知识点&#xff08;AI Knowledge&#xff09;系列第一篇文章--激活函数。本文主要的目录如下&#xff1a;激活函数的定义为什么需要激活函数常见的激活函数1. 激活函数的定义…

Linux 定时执行shell 脚本

2019年第 85 篇文章&#xff0c;总第 109 篇文章本文大约2000字&#xff0c;阅读大约需要6分钟crontab 可以在指定的时间执行一个shell脚本以及执行一系列 Linux 命令。定时执行shell 脚本简单给出执行 shell 脚本的步骤。首先是编写一个测试脚本--test.sh# 创建脚本 $ vim tes…

RS(1)--10分钟了解什么是推荐系统

总第 110 篇文章&#xff0c;本文大约 3200 字&#xff0c;阅读大约需要 10 分钟2020 年第一篇技术文章&#xff0c;以一个新的系列开始--推荐系统&#xff08;Recommend System&#xff09;&#xff0c;第一篇文章会简单介绍推荐系统的定义和应用&#xff0c;目录如下&#xf…