玩轉數據結構<一> 數組

數組

  • 數組最大的優勢: 快速查詢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減半
相關文章
相關標籤/搜索