认识数组

认识数组

基本概念

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

线性表:

就是数据排成像一条线一样的结构。每个线性表上的数据最多只有前后两个方向。

数组,队列,链表,栈等也都是线性表结构。

img

非线性表:

树,堆,图等数据结构为非线性表结构。在非线性表中,数据之间并不是简单的前后关系。

img

连续的内存空间和相同类型的数据:

这是因为数组具备两个限制,所有数组才能随机访问,因为数组中的元素能知道其具体所在位点。但是这样有利也有弊,导致插入,删除等操作的时候,效率会低下,因为需要做大量的数据迁移工作。

那么数组是如何实现随机访问的呢?

由于数组的创建,会申请一个连续的内存空间,那么意味着每个元素都有自己的内存地址,且数组里的每个元素前后相连,计算机会根据内存地址来访问内存中的数据。

例如我们创建一个长度为10,类型为Int的数组。

int[] a = new int[10];
//a数组创建好了以后,分配到了一块连续的内存空间1000~1039,首地址为1000

img

当计算机需要随机访问数组中的某个元素的时候,它会如下的公式来计算出实际需要访问数据的内存地址。

a[i]_address = base_address + i * data_type_size,其中i为偏移量。在上述数组中,由于类型是int,占4个字节,所以data_type_size为4。

根据上述的随机访问的解释,可以得出一个结论:数组支持随机访问,根据下标随机访问的时间复杂度为O(1)

注意:

数组是适合查找,但是查找的时间复杂度并不是O(1)。随机访问才是O(1)。随机访问的意思,按照我们常说的,就是根据下标或者索引返回元素。但是要查找或者判断某个元素在该数组中是否存在,即使是已经排好序的数组,利用二分查找法,时间复杂度也是O(logn)

插入和删除

插入

假设一个数组,长度为n,现在要在第k个位置插入一条元素。正常的逻辑应该是把第k个位置腾出来,第k个位置往后的元素需要往后移动一位,再将新元素赋值给第k个位置,这样呢就保证了新数组和之前的顺序一致。

如果k就是最后一位,那么插入的时间复杂度为o(1),不需要移动任何数据。但是如果k是第一位,那么插入的时间复杂度为o(n),需要移动所有数据。即最好时间复杂度为o(1),最坏为o(n),平均时间复杂度为(1+2+…n)/n=O(n)。

如果可以不需要保证顺序的一致,有个简单的方法就是,直接将第k个位置的数据搬移到最后,新数据赋值给第k个位置。(如下图所示)

img

删除

与插入操作类似,我们要删除第k个位置的元素,为了保证内存的连续性,也是需要做数据迁移工作的。如果删除数组末尾的数据,则最好情况时间复杂度为 O(1);如果删除开头的数据,则最坏情况时间复杂度为 O(n);平均情况时间复杂度也为 O(n)。

如果我们不是非得追求数组中的数据的连续性,我们可以将多次删除的操作,放在一起执行,效率可以提高很多。

例如:

a[10]中存储了8个元素:a,b,c,d,e,f,g,h。现在我们要一次删除abc三个元素。

img

为了避免三次都做了大量的数据迁移操作,我们可以先记录下已经删除的数据。每次删除操作都不是真正的搬移数据,只是记录数据已经被删除。当数组没有更多的空间存储数据时,我们在触发一次真正的删除操作,这样就大大减少了删除操作导致的数据迁移。

越界

一个数据长度为5,如果是java访问a[5],会报java.lang.ArrayIndexOutOfBoundException.

容器与数组

在java中,使用数组作为底层数据结构的容器还是蛮多的,用的最多的就是ArrayList,ArrayList支持动态扩容,将数组的很多操作细节都封装起来了。如果实现知道数据的大小,建议还是指定好ArrayList的大小,毕竟,扩容涉及到申请内存和数据迁移等操作,具有一定的开销。

那么选择容器还是数组呢?

  1. ArrayList是无法存储基本数据类型,虽说可以存储包装类型,但是自动装箱和拆箱等操作,是有一定性能消耗的。如果特别关注性能,并且需要使用基本数据类型,建议还是选用数组。
  2. 如果数据大小事先知道,并且操作很简单,建议是使用数组。
  3. 如果是多维,建议使用数组表示。
  4. 在普通的业务开发中,使用容器就已经足够了,省时省力。但是如果开发的是底层框架等,建议还是使用数组。

代码

public class ArrayTest {
    //定义整型数据data保存数据
    private int[] data;
    //定义数组的长度
    private int length;
    //定义数据的实际长度
    private int size;

    public ArrayTest(int capacity) {
        //初始化数据
        this.data = new int[capacity];
        //定义数据大小
        this.length = capacity;
        //初始化数组的实际大小
        //注意:可以不指定,默认值就是0
        //this.size = 0;
    }

    /**
     * 根据下标索引返回元素
     *
     * @param index 下标索引
     * @return 返回数组原色
     */
    public int find(int index) {
        //需要判断下标是否规范,如果不规范需要抛出异常
        if (index < 0 || index >= size) {
            throw new RuntimeException("index is illegal");
        }
        return data[index];
    }

    /**
     * 在index处添加元素value
     *
     * @param index 下标索引
     * @param value 需要插入的值
     */
    public void insert(int index, int value) {
        //如果数组的空间已经满了,则不允许插入元素
        if (size == length) {
            System.out.println("没有可插入的位置");
            return;
        }
        //判断下标是否合法,如果下标小于0或者大于real size即不合法
        /*
            如果实际大小为3,index为4,那么data[3]的数为几?
        */
        if (index < 0 || index > size) {
            throw new RuntimeException("index is illegal");
        }

        //index之后的数据往后移动一位
        for (int i = size; i > index; --i) {
            data[i] = data[i - 1];
        }

        //index合法
        data[index] = value;
        //实际大小 + 1
        size++;
    }

    /**
     * 根据下标删除元素
     *
     * @param index 下标索引
     */
    public void delete(int index) {
        //判断位置是否合法
        if (index < 0 || index >= size) {
            throw new RuntimeException("index is illegal");
        }

        //删除index处的元素,index之后的元素往前移一位
        for (int i = index + 1; i < size; i++) {
            data[i - 1] = data[i];
        }

        //实际大小 -1
        size--;
    }

    /**
     * 获取数组具体容量
     *
     * @return
     */
    public int length() {
        return length;
    }

    /**
     * 获取数组实际长度
     *
     * @return
     */
    public int size() {
        return size;
    }

    public void printAll() {
        for (int i = 0; i < size; i++) {
            System.out.print(data[i] + " ");
        }
        System.out.println("");
    }

    public static void main(String[] args) {
        ArrayTest array = new ArrayTest(5);
        array.printAll();
        array.insert(0, 3);
        array.insert(0, 4);
        array.insert(1, 5);
        array.insert(3, 9);
        array.insert(3, 10);
        array.insert(3, 11);
        array.delete(4);
        array.printAll();
        System.out.println(array.size());
    }

}

------------------------------- 整理自极客时间王争老师的数据结构和算法-------------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值