數據結構之【數組】

1 數據結構之數組

數組對於每一門編程語言來講都是重要的數據結構之一,固然不一樣語言對數組的實現及處理也不盡相同。Java 語言中提供的數組是用來存儲固定大小的同類型元素。你能夠聲明一個數組變量,如 numbers[100] 來代替直接聲明 100 個獨立變量 number0,number1,....,number99。數組一旦聲明完畢,長度就固定了。java

數組的聲明、建立和初始化。git

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。github

 

數組最大的優勢就是查詢快,使用索引來進行查詢;編程

數組的索引最好是有語義的。數組

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

運行結果:10this

 

修改成支持泛型

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

相關文章
相關標籤/搜索