数据结构之动态数组(Java)

需要注意的几个点:

1.泛型    使用泛型声明数组时 必须用Object类 然后强转为泛型

    public Array(int capacity){
        data = (E[])new Object[capacity];
        size=0;
    }

2.动态数组

扩容 缩容    定义私有的resize函数。体现了Java的封装性

    //动态数组
    private void resize(int newCapactiy) {
        E[] newData = (E[]) new Object[newCapactiy];
        for(int i=0;i<size;i++){
            newData[i] = data[i];
        }
        data = newData;
    }
在移除元素时 当数组容量size小于等于数组长度的一半时,进行缩容。
    //删除指定位置元素e  返回删除的元素
    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--;
        data[size] = null;            //loitering objects != memory leak     闲逛的对象 != 内存泄漏

        //数组缩容
        if(size ==data.length/2)
            resize(data.length/2);

        return ret;
    }

完整代码

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;
    }

    //无参构造函数 默认数组容量是10
    public Array(){
        this(10);
    }

    public int getSize(){
        return size;
    }

    public int getCapacity(){
        return data.length;
    }

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

    public void addLast(E e){

        add(size,e);
//        if(size==data.length){
//            throw new IllegalArgumentException("AddLast failed. Array is full.");
//        }
//        data[size] = e;
//        size++;
    }
    public  void addFirst(E e){
        add(0,e);
    }

    //在index位置 插入新的元素e
    public void add(int index, E e) throws IllegalArgumentException {

        if(index<0 ||  index>size ){
            throw new IllegalArgumentException("Add failed. Require index>=0 and index<=size.");
        }

        if(size ==data.length){
           // throw new IllegalArgumentException("Add failed. Array is full");
            resize(2*data.length);
        }

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



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

    //查找元素e所在的索引  如果不存在元素e  则返回-1
    public int find(E e){

        for(int i=0;i<size;i++){
            if(data[i].equals(e)){
                return i;
            }
        }
        return -1;
    }

    //删除指定位置元素e  返回删除的元素
    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--;
        data[size] = null;            //loitering objects != memory leak     闲逛的对象 != 内存泄漏

        //数组缩容
        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
    public boolean  removeElement(E e){
        int index = find(e);
        if(index!=-1){
            remove(index);
            return true;
        }
        return false;
    }

    //获取索引为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("Get Failed.  Index is illegal.");
        data[index] = e;
    }

    //动态数组
    private void resize(int newCapactiy) {
        E[] newData = (E[]) new Object[newCapactiy];
        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();
    }

}

动态数组时间复杂度:

  • 添加操作  

addLast(e)       O(1)        最坏情况需要resize    时间复杂度则变为O(n)

addFirst(e)       O(n)

add(index,e)     O(n/2)---->O(n)                resize(e)    O(n)

  • 删除操作

removeLast(e)    O(1)

removeFirst(e)    O(n)

remvoe(index,e)   O(n/2)---->O(n)            resize(e)   O(n)

  • 修改操作

set(index,e)     O(1)   支持随机访问

  • 查找操作

get(index)     O(1)

contains(e)   O(n)

find(e)           O(n)

  • 总结:
  1. 增: O(n)
  2. 删: O(n)
  3. 改: 已知索引是O(1);   未知索引是O(n)
  4. 查: 已知索引是O(1);   未知索引是O(n)

PPPPPS:resize的复杂度分析     addLast的均摊复杂度为O(1)

假设当前capacity = 8 并且每一次添加操作都使用addLast 

1    1    1    1    1    1    1    1      8+1    ( 前面八次添加满后 使用resize  赋值八次  然后再添加一次 ) 

9次addLast操作,触发了1次resize , 总共进行了17次基本操作(给一个空间附上一个值   9次添加操作  和 8次元素转移的操作)

所以,平均每次addLast操作,进行2次基本操作(17/9)...........

推广到n........

假设当前capacity = n 并且每一次添加操作都使用addLast,   

n次 addLast 操作      触发resize (n+1次基本操作)     所以总共进行 2n+1 次基本操作

所以,平均每次addLast操作,进行了2次基本操作( 2n+1 / n+1).......................不可能每次addLast操作都会触发resize,综合考虑    平均每次addLast操作,进行了2次基本操作。  这样均摊计算,addLast操作时间复杂度是O(1) 级别的 !!

同理  removeLast操作 均摊时间复杂度也为O(1)

PPPPPPPPPPPS:复杂度震荡

当我们同时看addLast 和 removeLast操作:

若一个数组的capacity = n   且size=n  此时再调用addLast  显然需要扩容resize O(n)  若马上进行removeLast操作 又需要缩容resize  O(n)     再addLast     再removeLast .............  这样每次addLast  removeLast  都会触发resize 就会造成复杂度震荡达到了O(n)这个级别!!!

造成这个问题的原因在于: removeLast时 resize过于着急 (Eager策略)

解决方案:Lazy策略

例如:   若数组capacity=n   size=n 若进行addLast操作     则会触发resize 操作    此时capacity = 2*n   size = n+1   此时若进行removeLast操作时 size--; 但   当 size == capactiy / 4 时   再触发resize操作(将 capacity == capacity / 2)       则可以解决。  .   

猜你喜欢

转载自blog.csdn.net/Hurricane_m/article/details/88175737