玩转数据结构<一> 数组

数组

  • 数组最大的优势: 快速查询java

  • 数组最好应用于"索引有语意"的状况算法

  • 但并不是全部有语意的索引都适用于数组数组

  • 数组也能够处理“索引没有语意”的状况bash

  • 封装数组数据结构

public class Array{
    private int[] data;
    private int size;
    //构造函数,传入数组的容量capacity 构造函数
    public Array(int capacity){
        data = new int[capacity];
        size = 0;
    }
    //无参数的构造函数,默认数组的容量capacity = 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(int e){
        add(size,e);
    }

    public void addFirst(int e){
        add(0,e);
    }

    //在第index个位置插入一个新元素e
    public void add(int index, int e){
        if(size == data.length){
            throw new IllegalArgumentException("Add failed. Array is full.");
        }
        

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

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

    //获取index索引位置的元素为e
    int get(int index){
        if(index < 0 || index >= size){
            throw new IllegalArgumentException("Get failed. Index is illegal.");
        }
        return data[index];
    }
    
    //修改index索引位置的元素为e
    void set(int index, int e){
        if(index < 0 || index >= size){
            throw new IllegalArgumentException("Get failed. Index is illegal.");
        }
        data[index] = e;
    }
    
    //查找数组中是否有元素e
    public boolean contains(int e){
        for(int i = 0 ; i < size; i ++){
            if(data[i] == e){
                return true;
            }
        }
        return false;
    }
    
    //查找数组中元素e所在的索引,若是不存在元素e,则返回-1
    public int find(int e){
    for(int i = 0 ; i < size; i ++){
            if(data[i] == e){
                return i;
            }
        }
        return -  1;  
    }
    
    //从数组删除index位置的元素,返回删除的元素
    public int remove(int index){
          if(index < 0 || index >= size){
          throw new IllegalArgumentException("Get failed. Index is illegal.");
      } 
      
      int ret = data[index];
      for(int i = index + 1; i < size; i++){
          data[i - 1] = data[i];
      }
      size --;
      return ret;
    }
    
    //从数组中删除最后一个元素,返回删除的元素
    public int removeFirst(){
        return remove(0);
    }    
    
    //从数组中删除最后一个元素,返回删除的元素
    public int removeLast(){
        return remove(size - 1);
    }
    
    // 从数组中删除元素e
    public void removeElement(int e){
        int index = find(e);
        if(index != -1){
            remove(index);
        }
    }
    
    
   
  @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();
    }
}

复制代码
  • 调用封装数组
public static void mai(String[] args){
Array arr = new Array(20);
 
}
复制代码

泛型

  • 让咱们的数据结构能够放置“任何”数据类型app

  • 不能够是基本数据类型,只能是类对象 (boolean,byte,char,short,int,long,float,double)ide

  • 每一个基本数据类型都有对应的包装类 (Boolean,Byte,Char,Short,Int,Long,Float,Double)函数

  • 泛型封装数组ui

public class Array<E>{
    private E[] data;
    private int size;
    //构造函数,传入数组的容量capacity 构造函数
    public Array(int capacity){
        data = (E[])new Object[capacity];
        size = 0;
    }
    //无参数的构造函数,默认数组的容量capacity = 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);
    }

    public void addFirst(E e){
        add(0,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++;
    }

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

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

    //查找数组中是否有元素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;
    }

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

        E ret = data[index];
        for(int i = index + 1; i < size; i++){
            data[i - 1] = data[i];
        }
        size --;
        //此时size--后,size仍是指向一个对象,须要java回收机制回收此对象,释放资源 loitering objects != memory leak
        data[size] = null;

        //layz 惰性缩减容量
        if(size == data.length / 4 && data.length /2 != 0){
            resize(data.length / 2);
        }
        return ret;
    }

    //从数组中删除最后一个元素,返回删除的元素
    public E removeFirst(){
        return remove(0);
    }

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

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

    public E getLast(){
        return get(size - 1);

    }
    public E getFirst(){
        return get(0);

    }

    @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();
    }

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

}

复制代码
  • 调用泛型封装数组
public static void mai(String[] args){
Array<Integer> arr = new Array<>(20);
 
}
复制代码

动态数组

简单复杂度分析

  • O(1) ,O(n),O(nlogn),O(n^2)
  • 大O简单描述的是算法的运行时间和输入数据之间的关系

*O(n) n是nums中的元素个数 算法和n呈线性关系 *为何要用大O,叫作O(n)? 忽略常数。实际时间 T=c1 * n+c2this

  • 渐进时间复杂度 描述n趋近于无穷的状况
public static int sum(int [] nums){
    int sum = 0;
    for(int num: nums){
        sum += num;
        return num;
    }
}

复制代码
  • 添加操做

  • addLast(e) O(1)

  • addFirst(e) O(n)

  • add(index,e) O(n/2) = O(n)

  • resize O(n)

  • 整体而言 添加操做是O(n) 严格计算出是须要一些几率论知识

  • 删除操做

  • removeLast(e) O(1)

  • removFirst(e) O(n)

  • remove(index,e) O(n/2) = O(n)

  • resize O(n)

  • 整体而言 删除操做是O(n)

  • 修改操做

  • set(index,e) O(1n)

  • 查找操做

  • get(index) O(1)

  • contains(e) O(n)

  • find(e) O(n)

均摊复杂度和防止复杂度的震荡

均摊复杂度

  • 假设当前capacity = 8,而且每一次添加操做都使用addLast,9次addLast操做,触发resize,总共进行了17次基本操做
  • 平均,每次addLast,进行2次基本操做
  • 假设capacity = n,n+1次addLast,触发resize,总共进行2n+1次基本操做,平均,每次addLast操做,进行2次基本操做
  • 这样均摊计算,时间复杂度是O(1)
  • 一样,removeLast操做,时间复杂度也是O(1)

复杂度震荡

  • 进行addLast以后接着进行removeLast操做,使操做进行扩容,缩容,每次操做都是O(n)
  • 问题缘由: removeLast时 resize过于着急(Eager)
  • 解决方案: Lazy
  • 当size == capacity /4时,才将capacity减半
相关文章
相关标签/搜索