数组

数组

数组的定义和特点

数组是一种线性结构,它将所有类型相同的元素存储在一块连续的内存空间中,可以通过索引去访问这块内存空间中的任意元素。

索引的本质是内存地址的偏移量,访问数组中的某个元素,就是通过数组首元素的内存地址 + 数组的元素大小 * 索引,得到索引的内存地址(也就是内存地址的偏移量),来访问数组中对应索引位置上的元素。

数组的常用操作

1. 初始化数组

数组可以通过两种方式初始化,无初始值或给定初始值。大多数编程语言中,无初始值时,会将数组元素初始化为0。

    /**
     * 初始化数组
     */
    public void init() {
        // 无初始值
        int[] array = new int[10];
        // 给定初始值
        int[] nums = {1, 2, 3, 4, 5};
    }

2.访问指定索引的元素

数组支持随机访问,可以在O(1)时间内访问任意元素,这是因为数组存储在连续的内存空间中,通过计算数组中要访问的索引所在的内存地址即可直接访问。

    /**
     * 随机访问元素
     * 
     * @param nums 数组
     * @return 访问的元素
     */
    public int randomAccess(int[] nums) {
        int randomIndex = ThreadLocalRandom.current().nextInt(0, nums.length);
        int num = nums[randomIndex];
        return num;
    }

3. 遍历元素

遍历数组,即访问数组中的每个元素。

    /**
     * 遍历数组
     */
    public void traverse(int[] nums) {
        // 通过索引遍历数组
        for (int i = 0; i < nums.length; i++) {
            System.out.println(nums[i]);
        }

        // 直接遍历数组
        for (int num : nums) {
            System.out.println(num);
        }
    }

4. 查找指定元素

遍历数组查找某个元素,这是一种线性查找,时间复杂度为O(n)

    /**
     * 查找数组中的元素,并返回索引
     *
     * @param nums   数组
     * @param target 要查找的元素
     * @return 元素的索引
     */
    public int find(int[] nums, int target) {
        for (int i = 0; i < nums.length; i++) {
            if (target == nums[i]) {
                return i;
            }
        }
        return -1;
    }

5. 插入元素

向数组中某个位置插入元素,需要将该位置以及该位置后的所有元素都向后移动一位,时间复杂度为O(n)。由于数组长度固定,插入可能会导致末尾元素丢失。

插入操作存在的缺点:

  • 时间复杂度高:数组的插入和删除的平均时间复杂度均为 O(n) ,其中n为数组长度。
  • 丢失元素:由于数组的长度不可变,因此在插入元素后,超出数组长度范围的元素会丢失。
  • 内存浪费:我们可以初始化一个比较长的数组,只用前面一部分,这样在插入数据时,丢失的末尾元素都是“无意义”的,但这样做会造成部分内存空间浪费。
    /**
     * 向数组中插入元素
     *
     * @param nums  数组
     * @param index 插入的位置
     * @param value 插入的值
     */
    public void insert(int[] nums, int index, int value) {
        // 把索引index以及之后的元素都往后移动一位
        for (int i = nums.length - 1; i < index; i--) {
            nums[i] = nums[i - 1];
        }
        // 将value赋值给index处的元素
        nums[index] = value;
    }

6. 删除元素

删除数组中某个元素,需要将该位置后的所有元素都向前移动一位,时间复杂度为O(n)。

删除操作存在的缺点:

时间复杂度高: 删除操作的平均时间复杂度为O(n),其中n为数组长度。

    /**
     * 删除数组index位置上的元素
     *
     * @param nums 数组
     * @param index 要删除的元素的位置
     */
    public void delete(int[] nums, int index) {
        // 把index位置后的所有元素都往前移动一位
        for (int i = index; i < nums.length; i++) {
            nums[i] = nums[i - 1];
        }
    }

7. 扩容

由于大多数编程语言数组长度不可变,扩容需要创建一个更大的数组,并将原数组复制过去,时间复杂度为O(n)。

    /**
     * 扩容
     * 
     * @param nums 要扩容的数组
     * @param newSize 扩容后数组的长度
     * @return 扩容后的数组
     */
    public int[] expand(int[] nums, int newSize) {
        int[] newNums = new int[newSize];
        for (int i = 0; i < nums.length; i++) {
            newNums[i] = nums[i];
        }
        return newNums;
    }

数组的优点与局限性

数组的优点:

  • 空间效率高:数组存储在连续的内存空间中,不需要像链表那样需要额外的空间来存储元素之间的关系。
  • 随机访问:数组支持随机访问,可以在O(1)时间内访问任意元素。
  • 内存连续:数组存储在连续的内存空间中,访问速度快。

数组的局限性:

  • 插入和删除操作效率低:当数组中元素较多时,插入和删除操作需要移动大量元素,时间复杂度为O(n)。
  • 长度固定:数组初始化后长度固定,扩容需要创建一个更大的数组,并将原数组复制过去,开销很大,时间复杂度为O(n)。
  • 空间浪费:如果数组分配的大小超过实际所需,那么多余的空间就被浪费了。

数组的应用场景

  • 随机访问:根据数组随机访问的特性,当我们需要随机抽取一些样本时,可用使用数组存储样本。
  • 排序和查找:数组是排序和搜索算法最常用的数据结构。快速排序、归并排序、二分查找等都主要在数组上进行。
  • 数据结构实现:数组可以用于实现栈、队列、哈希表、堆、图等数据结构。
  • 其他:机器学习、图像处理、音频处理等。
posted @ 2025-03-18 22:18  Wings~  阅读(25)  评论(0)    收藏  举报