【学习】数据结构与算法之美——基础篇笔记 数组


05 数组

什么是数组

  • 数组是线性表中的一种
  • 数组使用连续的存储空间,存储一组相同类型的数据

数组的下标随机访问

通过寻址公式,计算出该元素存储的内存地址
a[i]_address = base_address + i * data_type_size

值得注意的是,数组的的内存地址分配是[0]在低位,[n-1]在高位。
数组内存

如果数组从1开始计数,那么内存地址计算就会变成
a[i]_address = base_address + (i-1) * data_type_size

相当于每次随机访问数组元素都多了一次减法运算,对于 CPU 来说,就是多了一次减法指令。
所以当然还是0开始合适!

二维数组

二维数组大小为m*n,该元素存储的内存地址
a[i][j]_address = base_address + (i * n + j)*data_type_size

@Neuject 同学解释:
在这里插入图片描述

低效的“插入”和“删除”与巧思

1. 插入操作

一般看来,插入元素到数组的第k位,要将数组k~n位后移。这样的平均时间复杂度是 1 + 2 + . . . + n n → O ( n ) \frac{1+2+...+n}{n} \to O(n) n1+2+...+nO(n),因为在每个位置插入元素的概率是一样的。

  • 一种巧妙的插入方法:
    直接将第 k 位的数据搬移到数组元素的最后,把新的元素直接放入第 k 个位置。时间复杂度降为 O ( 1 ) O(1) O(1)
    在这里插入图片描述

2. 删除操作

在我之前看来,删除一个元素,就要把它后面的所有元素都向前挪,不然中间就会出现空洞,内存就不连续了。这样操作的平均情况时间复杂度也为 O ( n ) O(n) O(n)

  • 一种巧妙的删除方法:
    将多次删除操作集中在一起执行!每次的删除操作并不是真正地搬移数据,只是记录数据已经被删除。当数组没有更多空间存储数据时,再触发执行一次真正的删除操作。此算法类似JVM的标记清除垃圾回收算法的核心思想。
    在这里插入图片描述

注意访问越界问题

首先,我提醒自己平时写代码和刷题时要注意这些边界条件~

再来,看以下这段C语言代码。在C语言中,只要不是访问受限的内存,所有的内存空间都是可以自由访问的。数组越界在 C 语言中是一种未决行为,并没有规定数组访问越界时编译器应该如何处理。当然,其它语言就可能会报错。
在这里插入图片描述
用图来解释以上代码的死循环,就是程序内存分布问题,这里i和a为局部变量,被储存在栈区,栈区是从高地址往地址值扩展的。
在这里插入图片描述

附操作系统笔记,程序内存分布:
程序内存分布

容器 VS 数组

针对数组类型,很多语言都提供了容器类,如 Java 中的 ArrayList、C++ STL 中的 vector

容器的优点:

  • 将很多数组操作的细节封装起来,如数组插入、删除数据时需要搬移其他数据等
  • 支持动态扩容,每次存储空间不够的时候,它都会将空间自动扩容为 1.5 倍大小

注意,对于扩容大小,gcc是2倍,VS是1.5倍。两者的区别是,2倍扩容时间复杂度更优,可以保证时间复杂度 O ( n ) O(n) O(n) ,而1.5倍扩容时,空间可重用。(具体讨论看我另一篇文章:C++ vector 关于容器扩容思考

虽然扩容很方便,但是它涉及内存申请和数据搬移,是比较耗时的。所以,如果事先能确定需要存储的数据大小,最好在创建的时候事先指定数据大小

何时用数组?

  • 数据大小已知,操作简单,可直接使用数组
  • 表示多维数组时比较直观,像 int a[][],而不是 vector<vector<int>> a

总结

  • 数组用一块连续的内存空间,来存储相同类型的一组数据
  • 最大的特点就是支持随机访问,但插入、删除操作也因此变得比较低效,平均情况时间复杂度为 O ( n ) O(n) O(n)
  • 对于业务开发,可直接使用容器,省时省力,但会损耗一丢丢性能
  • 对于底层开发,比如开发网络框架,性能的优化需要做到极致,则选用数组合适


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据结构与算法是计算机科学和软件工程领域中非常重要的基础知识。数据结构是指组织和存储数据的方式,而算法则是解决问题的一系列步骤。在这里,我将简要介绍数据结构与算法基础知识。 1. 数组(Array):是一种线性数据结构,可以存储相同类型的元素。数组的特点是可以通过索引快速访问元素。 2. 链表(Linked List):也是一种线性数据结构,不同于数组,链表的元素在内存中可以不连续存储,每个元素包含一个指向下一个元素的指针。 3. 栈(Stack):是一种后进先出(LIFO)的数据结构,只能在栈的一端进行插入和删除操作。 4. 队列(Queue):是一种先进先出(FIFO)的数据结构,只能在队列的一端进行插入操作,在另一端进行删除操作。 5. 树(Tree):是一种非线性数据结构,由节点和边组成。树的一个节点可以有多个子节点。 6. 图(Graph):也是一种非线性数据结构,由节点和边组成。不同于树,图中的节点之间可以有多个连接。 7. 排序算法:常见的排序算法包括冒泡排序、选择排序、插入排序、快速排序、归并排序等,它们用于将一组元素按照特定的顺序进行排列。 8. 查找算法:常见的查找算法包括线性查找、二分查找等,它们用于在一组元素中查找特定的值。 以上只是数据结构与算法基础知识,还有许多其他重要的概念和算法,如哈希表、堆、图算法等。掌握数据结构与算法基础知识可以帮助我们更好地理解和解决实际的计算机问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值