谈谈数组

如何实现随机访问

 对于数组,相信绝大多数编程人员都是再熟悉不过的,用官方的话来介绍数组就是这句:数组(Array)是一种线性表数据结构,它用一组连续的内存空间,来存储一组具有相同数据类型的数据。

这一句非常官方的话涵盖着下面几个知识点:线性表、连续的内存空间和相同数据类型

线性表在前面博客提到过,字面意思就是:线性表就是数据排成像一条线一样的结构。每个线性表上的数据最多只有前后两个方向,除了数组外链表、队列、栈都是线性结构。

而与他对立的非线性表则不同,主要包含数、堆、图等,在逻辑结构上很清晰的就可以分辨出来。

 第二个知识点连续的内存空间和相同数据类型,也正是因为这两个特点,所以使得数组也具有顺序表的随机访问特性,但也因为这两个特性,使得数组操作变得非常低效,要想在数组中删除,插入元素不得不搬移大量的数据。

再谈谈数组是如何实现随机访问,例如拿一个长度为20的int型数组,如下图所示:

记内存首块地址为100,如果存储一个存储20个整型数据的数组,一个就是4字节算下来,可以得到上述图中的内存地址,计算机访问内存单元的方法就是如此,基本公式就是:a[i]=start_address+i*data_size,公式比较好理解。

数组的插入和删除

先看插入操作,先分析下,如果我们要将一个元素插入到长度为n的数组中的第k个位置,当然此时第k~n的元素都会后挪一位,此时复杂度会是多少?

显然,若k=1复杂度为o(n),若k=n复杂度为o(1),平均复杂度算来,由于每个位置插入的概率相同,故平均复杂度为(1+2+3+......+n)/n=o(n),当然这是建立在数组已经有序的基础上,如果数组是无序的又该如何呢?

在这种情况下其实有种更简单的办法,例如我们想把一个元素插入到第k个位置,可以选择先把a[k]取出来放到a[n+1]的位置,然后再把元素插入到a[k]位置,这样就很好的避免了大量的元素搬迁。

再看删除操作,类似于上面的插入,删除数组开头元素会达到最坏复杂度o(n),删除数组尾的元素则是最好时间复杂度o(1),平均复杂度也是o(n)

其实这也引起了一个思考,其实我们并不一定需要刻意追求数组数据的连续性,如果我们把删除操作放在一起执行效率其实会高一些,其实这就是jvm标记清除垃圾回收机制的核心内容例如下图:

 对于一个可以存储10元素的数组,若我们想要删除1、2、3、4位的元素,可以先依次标记元素,但不是真正的删除,只是标记,要插入元素则在数组后面插入即可当数组放不下元素的时候再统一的执行删除操作,删除已经标记的元素,然后继续重复此步骤直到操作完毕。

数组越界问题

int main()
{
    int a[3] = {0};
    for(int i=0; i<=3; i++)
    {
        a[i] = 0;
        printf("hello world\n");
    }
    return 0;
}

给段如图所示的代码,你如果认为这段代码打印三行hello world那就错了,因为a数组不存在a[3]这个元素,这是个典型的数组越界访问的问题,那么答案究竟是什么呢?大家其实可以不妨一试,最后的答案是无限打印hello world,熟悉内存机制的话其实这个是比较好理解的一个问题,数组越界其实在c语言中是一种未决行为,按照上面给的内存计算公式计算的话其实得到的地址是在存储变量i的位置,那么a[3]其实就相当于i=0,导致发生死循环,这是由于Linux进程的内存布局中,栈区在高地址空间,从高向低增长。变量i和arr在相邻地址,且i比arr的地址大,所以arr越界正好访问到i。当然,前提是i和arr元素同类型,否则那段代码仍是未决行为。

为什么数组要从0开始编号

 也算是提出的一个问题吧,是一个非常经典的题目,其实还是个内存计算问题,设想如果从1开始编号,那么计算内存地址的公式就会演变成a[i]=start_address+(i-1)*data_size,相比于之前的a[i]=start_address+i*data_size其实已经可以看出端倪来了,少了一条减法指令,从效率的角度看来这个其实是非常合理的一种看法,但这也只能算是一小部分吧,其实这个定义是C语言里面定义的,作为后面的编程语言其实也都算是对C的一中效仿吧,你要看成一种致敬也行,我认为主要就是这两部分原因,当然现在也并不是所以数组都是从0开始编号的,比如新晋之王Python,它可以支持负数下标。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值