• (一)动态数组


    目录


    前言

    java 内置的数组是静态的,我们可以基于它提供的静态数组,开发属于我们自己的动态数组,并且,保证其操作的复杂度,和 java 提供的静态数组,没有多大的差别,基本一致 ;


    已实现方法

    这里写图片描述


    思想

    底层使用 java 的内置数组;

    对外暴露两个构造器:一个接收一个参数,代表初始创建多大的数组 ;另一个无参构造器,底层默认创建大小为10的数组 ;

    动态的实现,在于 reSize() 方法,会对数组大小进行动态伸缩;

    比如我们最初的数组容量为 n ,当我们添加元素的个数大于数组容量的时候,我们就调用 reSzie() 方法,对数组进行扩容,这里,我们选择扩容到 2n

    当数组的实际元素个数等于 n/4 的时候,我们就对数组进行缩小,这里,我选择讲起缩小到 n/2 ;不要直接缩小到 n/4 ,会造成 复杂度震荡,具体原因,代码的注释中有讲,并其缩小的时候,注意传进去的参数,不能为 0 ,因为不能创建大小为 0 的数组 ;


    复杂度的分析(均摊复杂度、复杂度震荡)

    关于复杂度的分析,在代码眉头的注释里 ;


    java 代码

    package xin.ijava.array;
    
    
    /**
     * @author An
     * @description 自定义开发动态数组,基于java数组的二次封装
     * @date 2018年9月5日15:46:00
     * <p>
     * <p>
     * 动态数组 (实现很简单)
     * <p>
     * 时间复杂度的分析
     * <p>
     * ------------------------------- 增 ----------------------------
     * <p>
     * addLast()    O(1) ;
     * addFirst()   O(n) ;
     * add()        0(n/2) = O(n) ;
     * reSize()     0(n) ;
     * <p>
     * ------------------- addLast() 复杂度 --------------------
     * 其中 addLast()  ,会有几率触发 reSize(),但是我们还是认定它的复杂度是 O(1) 的,这里需要计算均摊复杂度 ;
     * 因为不是每次 addLast() 都会触发 reSize() ,假如 capacity 是 n ,那么 n+1 次 addLast() ,才会触发 reSize() ;
     * 均摊一下计算,就是 n+1 次 addLast() 操作,会进行 n+1+n 次基本操作,折算一下,一次 addLast() ,差不多 2 次
     * 基本操作,因此我们认定 addLast() 的复杂度就是 0(1) 的 ;
     * <p>
     * ------------------  复杂度震荡 --------------------------
     * addLast() 和 removeLast() ,这两个方法,都有几率触发 reSize() ;假如现在数组容量已经满了,再次
     * addLast() 就会触发 reSize() ,此时复杂度变为 O(n) ,然后,假如我们继续 addLast() 的话,那么之前的复杂度
     * 就会被均摊下去。但是,假如我们现在进行的是 removeLast() ,按照之前的逻辑,数组中的实际元素个数变为数组容量的
     * 一半时,就进行 reSize(),那么又会进行复杂度为 O(n) 的操作,假如一直 addLast() 和 removeLast() 交替下去,那么
     * 本来均摊复杂度应该为 O(1) 的操作,将会变为 O(n) ,均摊也不会降低复杂度了;这种情况,就叫复杂度震荡 ;
     * <p>
     * 解决方法就是,在缩容的时候,不要太着急,当数组的实际元素个数等于容量的一半的时候,不要进行 reSize() 操作,
     * 而是等实际元素个数等于容量的 1/4 的时候,再 reSize() ,并且 reSize() 也不是直接缩到当前容量的 1/4 ,不然进行
     * addLast() 又会进行 reSize(),我们让其 reSize() 缩到当前容量的 1/2 ;这样,就可以避免复杂度震荡了 ;
     * <p>
     * <p>
     * 结论:最坏情况下的复杂度是 O(n) 的 ;
     * <p>
     * <p>
     * <p>
     * ----------------------- 删 ----------------------
     * <p>
     * removeLast()            O(1) ; 跟 addLast() 一样 ,最后均摊下来都是 O(1)
     * removeFirst()           0(n) ;
     * remove()                0(n/2 + 1) = O(n)  ;
     * removeElement()         0(n + n ) = 0(n) ;
     * removeAllElement()      0( n * n ) ;
     * <p>
     * 结论:最坏情况下的复杂度是 O(n) 的 ;
     * <p>
     * <p>
     * <p>
     * <p>
     * ---------------------------- 改 -------------------------------
     * <p>
     * set()    0(1) ;
     * <p>
     * 结论:改,是数组的拿手好戏了,优势杠杠的 ;
     * <p>
     * <p>
     * <p>
     * ---------------------------- 查 -------------------------------
     * <p>
     * find()               O(n) ;
     * contains()           0(n) ;
     * get()                0(1) ;
     * <p>
     * <p>
     * <p>
     * 总结:我们的动态数组,复杂度还是很 ok 的,并且实现起来,也很方便 ;
     */
    @SuppressWarnings("unused")
    public class Array<E> {
        /**
         * 内部维护的数组,动态数组基于其开发
         */
        private E[] arr;
        /**
         * 数组中的存储的实际元素个数
         */
        private int size;
    
        /**
         * 创建用户给定的空间大小的数组
         */
        public Array(int capacity) {
    //       创建数组,不支持泛型数组,需要绕点弯子,创建泛型数组
            arr = (E[]) new Object[capacity];
    //        初始记录size的数量为 0
            size = 0;
        }
    
        /**
         * 默认开辟容量为 10 的数组 ;
         */
        public Array() {
            this(10);
        }
    
        /**
         * 判断数组 是否为空
         *
         * @return true  为空
         */
        public boolean isEmpty() {
            return size == 0;
        }
    
    
        /**
         * 向相应的下标位置上添加元素
         *
         * @param index 目标下标
         * @param e     目标元素
         */
        public void add(int index, E e) {
    
            //        数组是否满了
            if (size == arr.length) {
                reSize(2 * size);
            }
    //        下标 是否越界
            if (index < 0 || index >= arr.length) {
                throw new IllegalArgumentException("index is out of array ;");
            }
    
    //        添加元素
            for (int i = size; i > index; i--) {
                arr[i] = arr[i - 1];
            }
            arr[index] = e;
            size++;
        }
    
        /**
         * 向数组中追加元素
         *
         * @param e 目标元素
         */
        public void addLast(E e) {
            add(size, e);
        }
    
        /**
         * 向数组的第一个位置 添加元素
         *
         * @param e 目标元素
         */
        public void addFirst(E e) {
            add(0, e);
        }
    
        /**
         * 数组 是否 包含某一个元素
         *
         * @param e 目标元素
         * @return 包含返回 true
         */
        public boolean contains(E e) {
            for (int i = 0; i < size; i++) {
                if (arr[i].equals(e)) {
                    return true;
                }
            }
            return false;
        }
    
        /**
         * 数组 是否 包含某一个元素
         *
         * @param e 目标元素
         * @return 查到则返回对应的下标,不存在,则返回 -1 ;
         */
        public int find(E e) {
            for (int i = 0; i < size; i++) {
                if (arr[i].equals(e)) {
                    return i;
                }
            }
            return -1;
        }
    
        /**
         * 返回对应下标上的元素
         *
         * @param index 目标下标
         * @return 目标元素
         */
        public E get(int index) {
            //        下标 是否越界
            if (index < 0 || index >= arr.length) {
                throw new IllegalArgumentException("Get fail ; index is out of array ;");
            }
    
            if (index >= size) {
                throw new IllegalArgumentException("Get fail ; index is bigger than size ;");
            }
    
            return arr[index];
        }
    
        /**
         *  返回最后一个元素
         * @return 目标元素
         */
        public E getLast() {
            return get(size - 1);
        }
    
        /**
         *  返回第一个元素
         * @return 目标元素
         */
        public E getFirst() {
            return get(0);
        }
    
    
        /**
         * 返回数组实际元素个数
         *
         * @return
         */
        public int getSize() {
            return size;
        }
    
        /**
         * 返回数组容量
         *
         * @return
         */
        public int getCapacity() {
            return arr.length;
        }
    
        /**
         * 移除对应下标的元素
         *
         * @param index
         * @return
         */
        public E remove(int index) {
            E temp = arr[index];
            //        下标 是否越界
            if (index < 0 || index >= size) {
                throw new IllegalArgumentException("Add fail ; index is out of array ;");
            }
            if (isEmpty()) {
                throw new IllegalArgumentException("Remove fail ; array is empty");
            }
    
            for (int i = index; i < size - 1; i++) {
                arr[i] = arr[i + 1];
            }
            size--;
    
            arr[size] = null  ;
    
            if (size == arr.length / 4 &&  arr.length / 2 != 0 ) {
                reSize(arr.length / 2);
            }
    
            return temp;
        }
    
        /**
         * 对数组长度进行动态的变化;
         *
         * @param size
         */
        private void reSize(int size) {
            E[] es = (E[]) new Object[size];
            for (int i = 0; i < this.size; i++) {
                es[i] = arr[i];
            }
            arr = es;
        }
    
        /**
         * 移除 匹配到的第一个目标元素
         *
         * @param e 目标元素
         * @return 移除返回true
         */
        public boolean removeElement(E e) {
            int index = find(e);
            if (index != -1) {
                remove(index);
                return true;
            }
            return false;
        }
    
        /**
         * 移除掉 数组中所有的目标的元素
         *
         * @param e 目标元素
         */
        public void removeAllElement(E e) {
            while (removeElement(e)) {
            }
        }
    
        /**
         * 移除第一个
         *
         * @return
         */
        public E removeFirst() {
            return remove(0);
        }
    
        /**
         * 移除最后一个
         *
         * @return
         */
        public E removeLast() {
            return remove(size - 1);
        }
    
        /**
         * 更改目标位置的元素
         *
         * @param index
         * @param e
         */
        public void set(int index, E e) {
            add(index, e);
        }
    
        /**
         * 改变打印方式
         *
         * @return
         */
        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append(String.format("array 's size is %d , capacity is %d ; 
    ", size, arr.length));
            builder.append("[ ");
            for (int i = 0; i < size; i++) {
                builder.append(arr[i]);
                if (i != size - 1) {
                    builder.append(",");
                }
            }
            builder.append(" ]");
            return builder.toString();
        }
    }
    
  • 相关阅读:
    js 进阶笔记
    Move Zeroes
    笔记
    前端笔记
    PAI-AutoLearning 图像分类使用教程
    Redis Key过期通知
    Ubuntu 安装配置 JDK+Tomcat+Nginx
    Linux常用指令总结
    使用Openssl创建证书
    Python 操作Excel
  • 原文地址:https://www.cnblogs.com/young-youth/p/11665677.html
Copyright © 2020-2023  润新知