数据结构与算法之美(二) 数组

一、定义 

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

1、什么是线性表:数据排成一条线,每个线性表上的数据最多只有前后两个方向。数组、链表、队列、栈均为线性表结构。与之对应的非线性表结构包括:二叉树、堆、图等。

2、连续的内存空间+相同类型的数据,保证了数组具有“随机访问”的特性。

二、如何实现随机访问?

以一个长度为10的int型的数组int[] a = new int[10]为例,计算机给数组a分配了一块连续的内存空间1000~1039,其中1000为首地址。注意int指的是int32类型,为4个字节(32位)。当计算机要随机访问数组中的某个元素时,可以基于寻址公式,直接找到这个元素的内存地址:

a[i]_address = base_address + i * data_type_size
其中,因为a为int数组,所以data_type_size = 4(字节)

数组和链表的区别:

数组支持随机访问,根据下标随机访问的时间复杂度为O(1),但是数组的插入和删除比链表要低效。

三、数组的插入和删除

3.1 为什么低效?

一般我们是用数组来存储一组同类型的数据,并不需要考虑元素的顺序。

但是如果数组是有序的,并且希望插入元素后,数组仍旧保持有序,这时插入和删除元素都比较低效:

假设数组的长度为n, 现在我们要讲一个数据插到数组的第k个位置。为了把第k个位置挪出来,我们需要把k~n这部分的元素都顺序地往后挪一位。插入操作的时间复杂度是O(n),删除也是这样。

3.2 JVM标记清除垃圾回收算法

主要思想:数组中删除数据时,并不真正的删除,而是标记一下,等数组空间不够用时,再根据标记执行删除操作,进行数据的搬移工作。

四、数组的访问越界问题

前提:在C语言中,只要不是访问受限的内存,所有的内存空间都是可以自由访问的,数组越界在C语言中是一种未决行为,并没有规定数组访问越界时编译器应该如何处理。因为,访问数组的本质就是访问一段连续的内存,只要数组通过偏移计算得到的内存地址时可用的,那么程序就可能不会报任何错误。其他高级的程序语言,比如C++、Java、python,会对数组做越界检查,如果越界会直接抛出异常。

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;
}

上面这个C语言程序,会无限打印"hello world",为什么?

函数体内的局部变量是存储在栈上,且时连续压栈。在Linux进程的内存布局中,栈区在高地址空间,栈分配地址是从高到低的。而arr数组内部的地址是从低到高,arr[3]的地址恰好是i的地址。所以这里相当于会把i的值从3改成0,继续循环下去。

五、容器与数组

C++中定义一个数组,必须要是固定长度的:

//定义一个长度为5的数组
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};

//省略掉数组大小时,数组的大小为初始化时元素的个数, 下面是5
double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};

//访问数组某个元素,并赋值
balance[4] = 50.0;

C++的动态数组容器,是vector,支持动态扩容,一般在使用数组时,都会直接使用vertor。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值