数据结构--数组

1.概述

        在计算机科学中,数组是由一组元素(值或变量)组成的数据结构,每个元素有至少一个索引或键来标识,因为数组内的元素是连续存储的,所以数组中元素的地址,可以通过其索引计算出来。

小测试

    byte[] array = {1,2,3,4,5}

        已知 array 的数据的起始地址是 0x7138f94c8,那么元素 3 的地址是什么?

        答:0x7138f94c8 + 2 * 1 = 0x7138f94ca

随机访问性能

        即根据索引查找元素,时间复杂度是 O(1)

2.动态数组

java版本:

public class DynamicArray implements Iterable<Integer> {
    private int size = 0; // 逻辑大小
    private int capacity = 8; // 容量
    private int[] array = {};


    /**
     * 向最后位置 [size] 添加元素
     *
     * @param element 待添加元素
     */
    public void addLast(int element) {
        add(size, element);
    }

    /**
     * 向 [0 .. size] 位置添加元素
     *
     * @param index   索引位置
     * @param 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) {
            // 进行扩容, 1.5 1.618 2
            capacity += capacity >> 1;
            int[] newArray = new int[capacity];
            System.arraycopy(array, 0,
                    newArray, 0, size);
            array = newArray;
        }
    }

    /**
     * 从 [0 .. size) 范围删除元素
     *
     * @param index 索引位置
     * @return 被删除元素
     */
    public int remove(int index) { // [0..size)
        int removed = array[index];
        if (index < size - 1) {
            // 向前挪动
            System.arraycopy(array, index + 1,
                    array, index, size - index - 1);
        }
        size--;
        return removed;
    }


    /**
     * 查询元素
     *
     * @param index 索引位置, 在 [0..size) 区间内
     * @return 该索引位置的元素
     */
    public int get(int index) {
        return array[index];
    }

    /**
     * 遍历方法1
     *
     * @param consumer 遍历要执行的操作, 入参: 每个元素
     */
    public void foreach(Consumer<Integer> consumer) {
        for (int i = 0; i < size; i++) {
            // 提供 array[i]
            // 返回 void
            consumer.accept(array[i]);
        }
    }

    /**
     * 遍历方法2 - 迭代器遍历
     */
    @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++];
            }
        };
    }

    /**
     * 遍历方法3 - stream 遍历
     *
     * @return stream 流
     */
    public IntStream stream() {
        return IntStream.of(Arrays.copyOfRange(array, 0, size));
    }
}
  • 这些方法实现,都简化了 index 的有效性判断,假设输入的 index 都是合法的

  • 空间占用

    • Java 中数组结构为

    • 8 字节 markword

    • 4 字节 class 指针(压缩 class 指针的情况)

    • 4 字节 数组大小(决定了数组最大容量是 $2^{32}$)

    • 数组元素 + 对齐字节(java 中所有对象大小都是 8 字节的整数倍12,不足的要用对齐字节补足)

    • 例如:

    • int[] array = {1, 2, 3, 4, 5};

    • 的大小为 40 个字节,组成如下

    • 8 + 4 + 4 + 5*4 + 4(alignment)

插入或删除性能

        头部位置,时间复杂度是 O(n)

        中间位置,时间复杂度是 O(n)

        尾部位置,时间复杂度是 O(1)(均摊来说)

3.二维数组

int[][] array = {
    {11, 12, 13, 14, 15},
    {21, 22, 23, 24, 25},
    {31, 32, 33, 34, 35},
};

内存图如下

  • 二维数组占 32 个字节,其中 array[0],array[1],array[2] 三个元素分别保存了指向三个一维数组的引用

  • 三个一维数组各占 40 个字节

  • 它们在内层布局上是连续

4.局部性原理

        这里只讨论空间局部性

        cpu 读取内存(速度慢)数据后,会将其放入高速缓存(速度快)当中,如果后来的计算再用到此数据,在缓存中能读到的话,就不必读内存了

        缓存的最小存储单位是缓存行(cache line),一般是 64 bytes,一次读的数据少了不划算啊,因此最少读 64 bytes 填满一个缓存行,因此读入某个数据时也会读取其临近的数据,这就是所谓空间局部性

        数组可以充分利用局部性原理,那么链表呢?、链表不行,因为链表的元素并非相邻存储

5.越界检查

java 中对数组元素的读写都有越界检查,类似于下面的代码

bool is_within_bounds(int index) const        
{ 
    return 0 <= index && index < length(); 
}

只不过此检查代码,不需要由程序员自己来调用,JVM 会帮我们调用

        以上这些笔记是我从黑马程序员2023年最新的数据结构教程中整理出来的,并非我本人所创,发表是为了将自己所学的内容进行巩固,也帮助大家进行知识梳理。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ray-国

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值