线性表-数组

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

为什么数组下标从0开始:

a[k]_address = base_address + k * type_size

这是数组的寻址公式,如果下标从1开始,寻址公式就变成

a[k]_address = base_address + (k-1)*type_size

这样每次随机访问数组元素都多了一次减法运算,对于 CPU 来说,就是多了一次减法指令。

数组最主要特点:

最大的特点就是支持随机访问,但插入、删除操作也因此变得比较低效,平均情况时间复杂度为 O(n)
注意:数组是适合查找操作,但是查找的时间复杂度并不为 O(1)。即便是排好序的数组,你用二分查找,时间复杂度也是 O(logn)。所以,正确的表述应该是,数组支持随机访问,根据下标随机访问的时间复杂度为 O(1)。

警惕数组的访问越界问题:

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 word”,而是会无限打印“hello world”。数组大小为 3,a[0],a[1],a[2],而我们的代码因为书写错误,导致 for 循环的结束条件错写为了 i<=3 而非 i<3,所以当 i=3 时,数组 a[3]访问越界,在 C 语言中,只要不是访问受限的内存,所有的内存空间都是可以自由访问的。
又由于函数体内的局部变量存在栈上,且是连续压栈。在Linux进程的内存布局中,栈区在高地址空间,从高向低增长。变量i和arr在相邻地址,且i比arr的地址大,所以arr越界正好访问到i。当然,前提是i和arr元素同类型,否则那段代码仍是未决行为(实际中具体内存递增分配还是递减分配,还要结合具体编译器考虑)。
像 Java 本身就会做越界检查,比如下面这几行 Java 代码,就会抛出 java.lang.ArrayIndexOutOfBoundsException。

int[] a = new int[3];
a[3] = 10;

容器(比如 Java 中的 ArrayList、C++ STL 中的 vector)和数组取舍:

以java为例容器的优点:

ArrayList 最大的优势就是可以将很多数组操作的细节封装起来。比如前面提到的数组插入、删除数据时需要搬移其他数据等。另外,它还有一个优势,就是支持动态扩容,我们完全不需要关心底层的扩容逻辑,ArrayList 已经帮我们实现好了,每次存储空间不够的时候,它都会将空间自动扩容为 1.5 倍大小。
相比之下,事先指定数据大小可以省掉很多次内存申请和数据搬移操作。


ArrayList<User> users = new ArrayList(10000);
for (int i = 0; i < 10000; ++i) {
  users.add(xxx);
}

以java为例数组的优点:

1.Java ArrayList 无法存储基本类型,比如 int、long,需要封装为 Integer、Long 类,而 Autoboxing、Unboxing 则有一定的性能消耗,所以如果特别关注性能,或者希望使用基本类型,就可以选用数组。
2. 如果数据大小事先已知,并且对数据的操作非常简单,用不到 ArrayList 提供的大部分方法,也可以直接使用数组。
3. 当要表示多维数组时,用数组往往会更加直观。比如 Object[][] array;而用容器的话则需要这样定义:ArrayList > array。

总结一句话:普通程序员这两者用起来没太大区别,底层框架开发工程师优选数组。

拓展:

JVM 的标记清除垃圾回收算法的核心理念:

JVM垃圾回收算法: 1.复制算法. 2.标记清除算法. 3.标记整理算法. 简单思想: 数组中删除数据时,并不真正的删除,而是标记一下,先不进行数据的搬移工作,等数组空间不够用时,我们再执行删除操作.进行数据的搬移工作. --> 这样可以减少因为删除操作导致的数据搬移. 这种思想在JVM垃圾回收算法的 标记清除算法中 也有体现. 第一遍扫描先标记垃圾对象,第二遍扫描再清除垃圾对象. --> 这种垃圾回收算法 容易产生内存碎片,导致出现虽然内存空间充足,但是无法放置大对象。

二维数组内存寻址公式:

对于 m * n 的数组,a [ i ][ j ] (i < m,j < n)的地址为:
address = base_address + ( i * n + j) * type_size

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值