基础数据结构
数组
概述
定义
在计算机科学中,数组是由一组元素(值或变量)组成的数据结构,每个元素有至少一个索引或键来标识
In computer science, an array is a data structure consisting of a collection of elements (values or variables), each identified by at least one array index or key
因为数组内的元素是连续存储 的,所以数组中元素的地址,可以通过其索引计算出来,例如:
1 int [] array = {1 ,2 ,3 ,4 ,5 }
知道了数组的数据 起始地址 $BaseAddress$,就可以由公式 $BaseAddress + i * size$ 计算出索引 $i$ 元素的地址
$i$ 即索引,在 Java、C 等语言都是从 0 开始
$size$ 是每个元素占用字节,例如 $int$ 占 $4$,$double$ 占 $8$
小测试
1 byte [] array = {1 ,2 ,3 ,4 ,5 }
已知 array 的数据 的起始地址是 0x7138f94c8,那么元素 3 的地址是什么?
答:0x7138f94c8 + 2 * 1 = 0x7138f94ca
空间占用
Java 中数组结构为
8 字节 markword
4 字节 class 指针(压缩 class 指针的情况)
4 字节 数组大小(决定了数组最大容量是 $2^{32}$)
数组元素 + 对齐字节(java 中所有对象大小都是 8 字节的整数倍[^12],不足的要用对齐字节补足)
例如
1 int [] array = {1 , 2 , 3 , 4 , 5 };
的大小为 40 个字节,组成如下
1 8 + 4 + 4 + 5*4 + 4(alignment)
随机访问性能
即根据索引查找元素,时间复杂度是 $O(1)$
动态数组
java 版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 public class DynamicArray implements Iterable <Integer> { private int size = 0 ; private int capacity = 8 ; private int [] array = {}; public void addLast (int element) { add(size, element); } public void add (int index, int element) { checkAndGrow(); if (index >= 0 && index < size) { System.arraycopy(array, index, array, index + 1 , size - index); } array[index] = element; size++; } private void checkAndGrow () { if (size == 0 ) { array = new int [capacity]; } else if (size == capacity) { capacity += capacity >> 1 ; int [] newArray = new int [capacity]; System.arraycopy(array, 0 , newArray, 0 , size); array = newArray; } } public int remove (int index) { int removed = array[index]; if (index < size - 1 ) { System.arraycopy(array, index + 1 , array, index, size - index - 1 ); } size--; return removed; } public int get (int index) { return array[index]; } public void foreach (Consumer<Integer> consumer) { for (int i = 0 ; i < size; i++) { consumer.accept(array[i]); } } @Override public Iterator<Integer> iterator () { return new Iterator <Integer>() { int i = 0 ; @Override public boolean hasNext () { return i < size; } @Override public Integer next () { return array[i++]; } }; } public IntStream stream () { return IntStream.of(Arrays.copyOfRange(array, 0 , size)); } }
这些方法实现,都简化了 index 的有效性判断,假设输入的 index 都是合法的
插入或删除性能
头部位置,时间复杂度是 $O(n)$
中间位置,时间复杂度是 $O(n)$
尾部位置,时间复杂度是 $O(1)$(均摊来说)