Array学习笔记

Array学习笔记


  1. Array是什么?

答:简单来说,就是数据的一种有序排列。我们把数据码排成一排进行存放,并且通过索引可以方便地获得对应位置的数据信息。索引可以分为有语意和无语意两种情况,一般来说,数组更适合有语意的应用场景。

  1. Array在内存是怎么样的?

答:一块数组容量大小的内存区域在jvm上分配,索引的物理地址应该是连续分布的。

自制数组

package Array;

public class Array<E> {

    private E[] data;
    private int size;

    //传入容量capacity的构造方法
    public Array(int capacity) {
        //泛型数组的初始化
        data = (E[]) new Object[capacity];
        size = 0;
    }

    //无参构造方法
    public Array() {
        this(10);
    }

    //获取数组的容量
    public int getCapacity() {
        return data.length;
    }

    //获取数组中元素的个数
    public int getSize() {
        return size;
    }

    //判断数组是否为空
    public boolean isEmpty() {
        return size == 0;
    }

    //在索引index处添加元素
    public void add(int index, E e) {
        //边界判断
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("Add failed. Required index >= 0 and index <= size");
        }

        //数组容量不足,重设容量
        if (size == data.length) {
            resize(2 * data.length);
        }

        //将index后的元素向后移移一位
        for (int i = size - 1; i >= index; i--) {
            data[i + 1] = data[i];
        }

        data[index] = e;

        //维护size
        size++;
    }

    //在第一个位置添加一个元素
    public void addFirst(E e) {
        add(0, e);
    }

    //在所有元素后添加一个元素
    public void addLast(E e) {
        add(size, e);
    }

    //获取索引为index的元素
    public E get(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Get failed. Index is illegal.");
        }

        return data[index];
    }

    //修改index索引位置的元素为e
    public void set(int index, E e) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Set Failed. Index is illegal.");
        }
        data[index] = e;
    }

    //查找数组中是否有元素e
    public boolean contains(E e) {
        for (int i = 0; i < size; i++) {
            //注意用equals别用 ==
            if (data[i].equals(e)) {
                return true;
            }
        }
        return false;
    }

    //查找元素e所在的索引,如果存在返回第一个索引,如果不存在,返回-1
    public int find(E e) {
        for (int i = 0; i < size; i++) {
            if (data[i].equals(e)) {
                return i;
            }
        }
        return -1;
    }

    //从数组中删除index位置上的元素,并返回其值
    public E remove(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Removed failed. Index is illegal.");
        }

        E ret = data[index];

        //将index后的所有元素全部向前挪一个位置
        for (int i = index + 1; i < size; i++) {
            data[i - 1] = data[i];
        }

        //维护size
        size --;

        //当删除后元素数量小到一定程度时,进行降容处理
        if (size == data.length / 2) {
            resize(data.length / 2);
        }

        return ret;
    }

    //从数组中删除第一个元素
    public E removeFirst() {
        return remove(0);
    }

    //从数组中删除最后一个元素
    public E removeLast() {
        return remove(size - 1);
    }

    //从数组中删除元素为e的元素(第一个e元素)
    public void removeElement(E e) {
        int index = find(e);
        if (index != -1) {
            remove(index);
        }
    }



    //重新给数组设定容量
    private void resize(int newCapacity) {
        //申请新容量的数组
        E[] newData = (E[]) new Object[newCapacity];

        //将旧数组中的数据拷贝到新数组
        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }

        data = newData;
    }

    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append(String.format("Array: size = %d , capacity = %d\n", size, data.length));
        res.append('[');
        for(int i = 0 ; i < size ; i ++){
            res.append(data[i]);
            if(i != size - 1)
                res.append(", ");
        }
        res.append(']');
        return res.toString();
    }
}

复杂度分析

最坏时间复杂度O(~)

大O描述的是算法的运行时间和输入数据之间的关系。
一般指渐近时间复杂度,描述n趋向于无穷的情况。

添加操作–O(n)

addLast(e)——-O(1)
addFirst(e)——O(n)
add(Index, e)—-O(n/2)=O(n)
运用概率论的知识。index随机1-n概率均为1/n,
计算运行时间期望。

删除操作–O(n)

removeLast(e)——-O(1)
removeFirst(e)——O(n)
remove(Index)—-O(n/2)=O(n)

修改操作–O(1)

set(index, e)

查找操作–O(1)

get(index)
contains(e)
find(e)

均摊时间复杂度

resizable(capacity)虽然为O(n),但是不是每次都会执行到的。
对于一个容量为8的数组来说,前8次增加都不会调用resizable,但是当第9次添加元素时,会调用一次resizable,一次resizable为8次操作。
总结地说,9次addLast操作(这里假设用的都是复杂度最低的addLast),触发resizable,总共进行了17次基本操作。
推广到n,也就是对于一个capacity=n的数组来说,进行n+1次操作,触发一次resizable,总共进行2n+1次操作。
也就是说,平均每次addLast操作,进行2次基本操作。

由此引入均摊复杂度(amortized time complexity–
addLast的均摊复杂度为O(1),removeLast同理。

复杂度震荡

对于容器为10的操作中,当进行第11次增加addLast时,capacity增加到20,可是当我们在这之后又进行移除removeLast操作的时候,capacity又将变成10。循环往复,频繁进行resizable操作的话,这样就会造成不必要的性能浪费。

原因:removeLast操作过于着急(Eager)
解决办法:Lazy。即增加的时候若容器capacity不够时,立刻增加capacity至原来的2倍。可是在减小容器capacity的时候,我们可以稍微lazy一点,当元素数量为capacity的1/4时我们才去减少capacity为原来的1/2。这样就可以有效解决震荡引起的性能损失。

笔记整理来源于liuyubobobo老师在慕课网的《玩转数据结构 从入门到进阶课程》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值