动态数组

守給你的承諾、 提交于 2020-02-26 12:55:06

知识点

实现属于自己的动态数组类

分析时间复杂度:

  • 增: O(n)
  • 删: O(n)
  • 改: 已知索引O(1);未知索引O(n)
  • 查: 已知索引O(1);未知索引O(n)

主要操作

一些变量定义:

data: 数组

size:当前数组中的元素个数
  • 添加操作

描述: 在指定索引 index 位置添加元素。

思路:

先检查索引是否越界,没有越界的情况下继续往下执行,否则抛异常。

size >= data.length 时,进行扩容操作,大小扩容为(data.length * 2)。

当前面两步都没问题的情况下,进行添加操作,先将 index 位置后的元素整体向后移动,移动完毕后再将要添加的元素添加到 index 位置。

最后 size 不要忘记加 1。

public void add(int index, E e) {
    if (index < 0 || index > size) {
        throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size.");
    }

    // 数组容量不够的话就进行扩容
    if (size >= data.length) {
        resize(2 * data.length);
    }

    for (int i = size - 1; i >= index; i--) {
        data[i + 1] = data[i];
    }

    data[index] = e;
    size++;
}
  • 删除操作

描述: 在指定索引 index 位置删除元素,并返回被删除元素。

思路:

同样先进行 index 是否越界,再决定是否往下执行。

之后先将要删除的元素赋值给一个临时变量,用于最后的返回。

再进行删除操作,具体操作就是将 index 位置后的元素整体向前移动。

操作完后,不要忘记 size 减 1。

这里因为元素整体向前移动,那么原来最后一个元素就没用了,这里可以将 data[size] = null

在最后返回那个删除元素之前,因为数组是动态数组,所以还要检查下是否需要进行缩容。这里的缩容条件是:当前数组中的元素个数小于等于总容量的四分之一并且当前总容量不能小于 3,即 size <= data.length / 4 && data.length / 2 != 0,只要满足此条件,则进行缩容操作。

最后,将被删除的元素返回。

public E remove(int index) {
    if (index < 0 || index >= size) {
        throw new IllegalArgumentException("Remove failed. Index is illegal.");
    }

    E ret = data[index];
    for (int i = index + 1; i < size; i++) {
        data[i - 1] = data[i];
    }
    size--;
    // loitering objects != memory leak
    data[size] = null;

    // 数组容量过多的话就进行缩小
    // 复杂度震荡
    if (size <= data.length / 4 && data.length / 2 != 0) {
        resize(data.length / 2);
    }

    return ret;
}
  • 扩容缩容操作

描述: 传入一个新的容量大小,进行扩容缩容操作。

思路: 新创建一个新的容量大小的数组,并将原来数组中的元素依次赋值给新的数组,并将原来数组的引用指向新的数组。

private void resize(int newCapacity) {
    E[] newData = (E[]) new Object[newCapacity];
    for (int i = 0; i < size; i++) {
        newData[i] = data[i];
    }
    data = newData;
}

代码

public class Array<E> {

    private E[] data;
    /**
     * 当前数组中的元素个数
     */
    private int size;

    /**
     * 构造函数,传入数组的容量capacity构造Array
     *
     * @param capacity 数组容量
     */
    public Array(int capacity) {
        data = (E[]) new Object[capacity];
        size = 0;
    }

    /**
     * 无参构造函数,默认数组的容量capacity=10
     */
    public Array() {
        this(10);
    }

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

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

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

    /**
     * 在第index个位置插入一个新元素e
     * <p>
     * 简单的复杂度分析:O(n/2) = O(n)
     * 严格计算需要一些概率论知识
     *
     * @param index 索引位置
     * @param e     要添加的新元素
     */
    public void add(int index, E e) {
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size.");
        }

        // 数组容量不够的话就进行扩容
        if (size >= data.length) {
            resize(2 * data.length);
        }

        for (int i = size - 1; i >= index; i--) {
            data[i + 1] = data[i];
        }

        data[index] = e;
        size++;
    }

    /**
     * 向所有元素前添加一个新元素
     * <p>
     * 简单的复杂度分析:O(n)
     *
     * @param e 要添加的新元素
     */
    public void addFirst(E e) {
        add(0, e);
    }
    
    /**
     * 向所有元素后添加一个新元素
     * <p>
     * 简单的复杂度分析:O(1)
     *
     * @param e 要添加的新元素
     */
    public void addLast(E e) {
        add(size, e);
    }

    /**
     * 获取index索引位置的元素
     * <p>
     * 简单的复杂度分析:O(1)
     *
     * @param index 索引位置
     * @return index 索引位置的元素
     */
    public E get(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Get failed. Index is illegal.");
        }
        return data[index];
    }

    /**
     * 获取最后一个元素
     *
     * @return
     */
    public E getLast() {
        return get(size - 1);
    }

    /**
     * 获取第一个元素
     *
     * @return
     */
    public E getFirst() {
        return get(0);
    }

    /**
     * 修改index索引位置的元素为e
     * <p>
     * 简单的复杂度分析:O(1)
     *
     * @param index 索引位置
     * @param 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
     * <p>
     * 简单的复杂度分析:O(n)
     *
     * @param e 要查找的元素
     * @return true | false
     */
    public boolean contains(E e) {
        for (int i = 0; i < size; i++) {
            if (data[i].equals(e)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 查找数组中元素e所在的索引,只返回找到的第一个元素e的索引位置,如果不存在元素e,则返回-1
     * <p>
     * 简单的复杂度分析:O(n)
     *
     * @param e 要查找的元素
     * @return 索引的位置
     */
    public int find(E e) {
        for (int i = 0; i < size; i++) {
            if (data[i].equals(e)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 从数组中删除index位置的元素,返回删除的元素
     * <p>
     * 简单的复杂度分析:O(n/2) = 0(n)
     *
     * @param index 索引的位置
     * @return 删除的元素
     */
    public E remove(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Remove failed. Index is illegal.");
        }

        E ret = data[index];
        for (int i = index + 1; i < size; i++) {
            data[i - 1] = data[i];
        }
        size--;
        // loitering objects != memory leak
        data[size] = null;

        // 数组容量过多的话就进行缩小
        // 复杂度震荡
        if (size <= data.length / 4 && data.length / 2 != 0) {
            resize(data.length / 2);
        }

        return ret;
    }

    /**
     * 从数组中删除第一个元素,返回删除的元素
     * 
     * 简单的复杂度分析:O(n)
     *
     * @return 删除的元素
     */
    public E removeFirst() {
        return remove(0);
    }

    /**
     * 从数组中删除最后一个元素,返回删除的元素
     * 
     * 简单的复杂度分析:O(1)
     *
     * @return 删除的元素
     */
    public E removeLast() {
        return remove(size - 1);
    }

    /**
     * 从数组中删除元素e,只删除第一个找到的元素e
     * 
     * 简单的复杂度分析:O(n)
     *
     * @param e 删除的元素
     */
    public void removeElement(E e) {
        int index = find(e);
        if (index != -1) {
            remove(index);
        }
    }

    /**
     * 打印数组中的所有元素
     *
     * @return 数组中的所有元素
     */
    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append(String.format("Array: size = %d, capacity = %d\n", size, getCapacity()));
        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(n)
     *
     * @param newCapacity 新数组的容量
     */
    private void resize(int newCapacity) {
        E[] newData = (E[]) new Object[newCapacity];
        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        data = newData;
    }

}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!