zoukankan      html  css  js  c++  java
  • 数据结构之【数组】

    1 数据结构之数组

    数组对于每一门编程语言来说都是重要的数据结构之一,当然不同语言对数组的实现及处理也不尽相同。Java 语言中提供的数组是用来存储固定大小的同类型元素。你可以声明一个数组变量,如 numbers[100] 来代替直接声明 100 个独立变量 number0,number1,....,number99。数组一旦声明完毕,长度就固定了。

    数组的声明、创建和初始化。

    package cc.myall.demo01;
    import org.junit.Test;
    public class demo01 {
    
        @Test
        public void test01(){
            int[] array = new int[10];
            int len = array.length;
            for(int i=0; i < len; i++ ){
                System.out.println(array[i]);  //默认值是0
            }  
        }
    }

    运行的结果就是打印出10个0。

    数组最大的优点就是查询快,使用索引来进行查询;

    数组的索引最好是有语义的。

    1.1 二次封装自己的数组

    基于java数组来二次封装自己的数组类(Array),使之成为动态数组,也就是java内置的ArrayList类。下面来自己实现一下。

    size指向的是第一个没有元素的位置。

    新建一个类Array,

    package cc.myall.demo01;
    public class Array {
        private int[] data; //先假定这个数组只能装int类型
        private int size;
        
        //构造函数,传入数组的容量来构造数组
        public Array(int capacity) {
            data = new int[capacity];
            size = 0;  //实际大小初始为0
        }
        public Array() {
            this(10);
        }
        //获取数组中元素的个数
        public int getSize() {
            return size;
        }
        
        // 获取数组的容量
        public int getCapacity() {
            return data.length;
        }
        
        //判断数组是否为空
        public boolean isEmpty() {
            return size == 0;
        }
    }

    测试:

    package cc.myall.demo01;
    import org.junit.Test;
    public class demo01 {    
        @Test
        public void test02(){
            Array arr = new Array();
            System.out.println(arr.getCapacity());
        }
    }

    运行结果:10

    修改为支持泛型

    public class Array<E> {
        private E[] data; //先假定这个数组只能装int类型
        private int size;
        
        //构造函数,传入数组的容量来构造数组
        public Array(int capacity) {
            data = (E[]) new Object[capacity];
            size = 0;  //实际大小初始为0
        }
        public Array() {
            this(10);
        }
        //获取数组中元素的个数
        public int getSize() {
            return size;
        }
        
        // 获取数组的容量
        public int getCapacity() {
            return data.length;
        }
        
        //判断数组是否为空
        public boolean isEmpty() {
            return size == 0;
        }
    }

    1.2 使数组具有增删查改功能

    addLast(int e) 时间复杂度O(1)

    在最后一个元素的后面添加一个元素,原理就是在size处添加,然后让size加1。在Array类中添加addLast函数。

    //在最后一个元素的后面添加元素
        public void addLast(E e) {
            if(size == data.length) {
                throw new IllegalArgumentException("添加失败,数组已满");
            }
            data[size] = e;
            size ++;
        }

    add(int index, int e) 时间复杂度O(n)

    在元素中间的某个位置插入元素,让改位置及以后的元素一次移动一个位置,即size-1位置的一道size处,以此类推,移动后如下图,就腾出一个位置来:

    这里说的腾出一个位置并不是说改位置为空,而是可以插入覆盖。代码如下

        //向指定index处添加元素
        public void add(int index, E e) {
            if(size == data.length) {
                throw new IllegalArgumentException("添加失败,数组已满");
            }
            if(index < 0 || index > size) {
                throw new IllegalArgumentException("添加失败,要求0<=index<=size");
            }
            for(int i = size-1; i >= index; i --) {
                data[i+1] = data[i];
            }
            data[index] = e;
            size ++;
        }

    再看addLast(int e) 时间复杂度O(1)

    可以利用add函数改写,此时的index就是size。

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

    addFirst(int e) 时间复杂度O(n)

    可以利用add函数改写,此时的index就是0。在数组的第一个元素出插入元素

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

    get set时间复杂度O(1)

        // 获取index索引处的元素
        public E get(int index) {
            if(index < 0 || index > size) {
                throw new IllegalArgumentException("添加失败,要求0<=index<=size");
            }
            return data[index];
        }

       

        // 改变index索引处的元素
        public void set(int index, E e) {
            if(index < 0 || index > size) {
                throw new IllegalArgumentException("添加失败,要求0<=index<=size");
            }
            data[index] = e;
        }

    contains()时间复杂度O(n)

    查找数组中是否包含元素e

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

    find() 时间复杂度O(n)

        //查找数组中元素e是否存在,返回索引,不存在返回-1
        public int find(E e) {
            for(int i = 0; i < size; i ++) {
                if(data[i].equals(e)){
                    return i;
                }
            }
            return -1;
        }

    remove() 时间复杂度O(n)

    删除指定位置的元素,并且返回删除的元素。原理和添加一个元素类似。

        

    // 删除指定位置index的元素
    
        public E remove(int index) {
            if(index < 0 || index > size) {
                throw new IllegalArgumentException("添加失败,要求0<=index<=size");
            }
            E ret = data[index];
            for(int i = index + 1; i < size; i ++) {
                data[i-1] = data[i];
            }
            size --;
            return ret;
        }

    1.3 动态数组的实现

    在往数组中添加元素的时候,如果数组已经满了,可以重新建一个新的较大容量的数组,把原来的数组的元素全部放入新数组中,这样就可以实现扩容。减少元素到一定程度就缩容。

       

     //向指定index处添加元素
        public void add(int index, E e) {
            if(index < 0 || index > size) {
                throw new IllegalArgumentException("添加失败,要求0<=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的元素
        public E remove(int index) {
            if(index < 0 || index > size) {
                throw new IllegalArgumentException("添加失败,要求0<=index<=size");
            }
            E ret = data[index];
            for(int i = index + 1; i < size; i ++) {
                data[i-1] = data[i];
            }
            size --;
            if(size == data.length / 2) {
                resize(data.length / 2);
            }
            return ret;
        }

     

    时间复杂度O(n)

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

    1.4 均摊时间复杂度和防止复杂度震荡

    实现动态数组之后,addLast操作的时间复杂度最坏是O(n)的。

    假设数组容量是n,n+1次操作触发一次扩容操作,总共2n+1次操作。平均每次addLast操作,进行两次基本操作。这样均摊计算,addLast操作的时间复杂度是O(1),在这个例子里,均摊计算比最坏计算要有意义。

    复杂度振荡产生:当addLast一个元素需要扩容时,复杂度是O(n),然后又马上删除一个元素,要缩容,时间复杂度也是O(n)。均摊的计算方法就不能用了,产生震荡。

    解决办法:让数据元素个数减少到容积的1/4时再缩容,仍缩为原来的一半。

     

       // 删除指定位置index的元素
        public E remove(int index) {
            if(index < 0 || index > size) {
                throw new IllegalArgumentException("添加失败,要求0<=index<=size");
            }
            E ret = data[index];
            for(int i = index + 1; i < size; i ++) {
                data[i-1] = data[i];
            }
            size --;
            if(size == data.length / 4 && data.length / 2 != 0) { // Lazy方式
                resize(data.length / 2);
            }
            return ret;
        }

    代码: https://github.com/zhang-anan/DataStructure/tree/master/src/cc/myall/demo01

     

  • 相关阅读:
    jetcache 二级缓存使用
    hutool-crypto 依赖 Aes加密,解密
    springboot下的logback-spring配置文件以及使用方式
    docker 实现多个端口映射
    zookeeper部署启动异常,8080端口被占用。
    docker tomcat 文件传递
    关于注解AOP,基于类和方法的实现
    idea 创建file找不到java文件时....
    idea 将项目代码提交到github中
    java第八天---多态、抽象、接口
  • 原文地址:https://www.cnblogs.com/zhang-anan/p/10086440.html
Copyright © 2011-2022 走看看