zoukankan      html  css  js  c++  java
  • 01:数组

    在大部分编程语言中,数组都是从0开始编号的,为什么数组要从0开始编号,而不是从1开始呢?

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

    线性表:数组、链表、栈、队列

    非线性表:二叉树、堆、图等    在非线性表中,数据之间并不是简单的前后关系

    如何实现随机访问?==》线性表;连续的内存空间和相同类型的数据

    根据下标随机访问数组元素==》a[i]_address = base_address + i * data_type_size

    data_type_size表示数组中每个元素的大小

    数组查找的时间复杂度==》二分查找:O(logn)

    数组支持随机访问,根据下标随机访问的时间复杂度为O(1)

    低效的“插入”和“删除”==》数组为了保持内存数据的连续性,会导致插入、删除这两个操作比较低效

    =============================================================

    如果在数组的末尾插入元素,就不需要移动数据了,这时的时间复杂度为 O(1)。

    如果在数组的开头插入元素,那所有的数据都需要依次往后移动一位,所以最坏时间复杂度是 O(n)。

    因为在每个位置插入元素的概率是一样的,所以平均情况时间复杂度为 (1+2+…n)/n=O(n)。

    =============================================================

    改进:如果数组中的数据是有序的,在某个位置插入一个新的元素时,就必须按照刚才的方法搬移 k 之后的数据。

    但是,如果数组中存储的数据并没有任何规律,数组只是被当作一个存储数据的集合。在这种情况下,如果要将某个数据插入到第 k 个位置,为了避免大规模的数据搬移,还有一个简单的办法就是,直接将第 k 位的数据搬移到数组元素的最后,把新的元素直接放入第 k 个位置。

    删除

    ============================================================

    要删除第 k 个位置的数据,为了内存的连续性,也需要搬移数据,不然中间就会出现空洞,内存就不连续了。

    和插入类似,如果删除数组末尾的数据,则最好情况时间复杂度为 O(1);如果删除开头的数据,则最坏情况时间复杂度为 O(n);平均情况时间复杂度也为 O(n)。

    在某些特殊场景下,并不一定非得追求数组中数据的连续性。如果将多次删除操作集中在一起执行,删除的效率会提高很多

    先记录下已经删除的数据。每次的删除操作并不是真正地搬移数据,只是记录数据已经被删除。当数组没有更多空间存储数据时,再触发执行一次真正的删除操作,这样就大大减少了删除操作导致的数据搬移。

    JVM标记-清除垃圾回收算法的核心思想

    ==========================================================

    数组越界:java.lang.ArrayIndexOutOfBoundsException

    容器:

    ArrayList 最大的优势就是可以将很多数组操作的细节封装起来。比如数组插入、删除数据时需要搬移其他数据等。

    另外,它还有一个优势,就是支持动态扩容。数组本身在定义的时候需要预先指定大小,因为需要分配连续的内存空间。如果我们申请了大小为 10 的数组,当第 11 个数据需要存储到数组中时,我们就需要重新分配一块更大的空间,将原来的数据复制过去,然后再将新的数据插入。如果使用 ArrayList,我们就完全不需要关心底层的扩容逻辑,ArrayList 已经帮我们实现好了。每次存储空间不够的时候,它都会将空间自动扩容为 1.5 倍大小。因为扩容操作涉及内存申请和数据搬移,是比较耗时的。所以,如果事先能确定需要存储的数据大小,最好在创建 ArrayList 的时候事先指定数据大小。

    ==========================================================

    从数组存储的内存模型上来看,“下标”最确切的定义应该是“偏移(offset)”

    如果用 a 来表示数组的首地址,a[0]就是偏移为 0 的位置,也就是首地址,a[k]就表示偏移 k 个 type_size 的位置,所以计算 a[k]的内存地址只需要用这个公式:a[k]_address = base_address + k * type_size

    但是,如果数组从 1 开始计数,那我们计算数组元素 a[k]的内存地址就会变为:a[k]_address = base_address + (k-1)*type_size

    从 1 开始编号,每次随机访问数组元素都多了一次减法运算,对于 CPU 来说,就是多了一次减法指令。

    二维数组内存寻址:

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

    address = base_address + ( i * n + j) * type_size

  • 相关阅读:
    外观模式
    享元模式
    装饰模式
    适配器模式
    组合模式
    典型用户模板与场景
    知识圈APP开发记录(十二)
    知识圈APP开发记录(十一)
    知识圈APP开发记录(十)
    周总结(七)
  • 原文地址:https://www.cnblogs.com/liushoudong/p/13507492.html
Copyright © 2011-2022 走看看