1. 程式人生 > 實用技巧 >簡單的資料結構—陣列

簡單的資料結構—陣列

1.什麼是陣列?

  陣列是有限個像同類型的變數所組成的有序集合,陣列中的每一個變數被稱為元素。陣列是最為簡單,最為常用的資料結構。

  正如軍隊裡的士兵存在編號一樣,陣列中的每一個元素也有著自己的下標,只不過這個下標是從0開始,一直到陣列長度-1。

  陣列的另一個特點,是在記憶體中順序儲存,因此可以很好的實現邏輯上的順序表。

2.陣列在記憶體中的順序儲存,具體是什麼樣子呢?

  記憶體是由一個個連續的記憶體單元組成的,每一個記憶體單元都有自己的地址。在這些記憶體單元中,有些被其他資料佔用了,有些是空閒的。陣列中的每一個元素,都儲存在小小的記憶體單元中,且元素之間緊密排列,既不能打亂元素的儲存順序,也不能跳過某個儲存單元進行儲存。

3.陣列的基本操作

  資料結構的操作無非就是增,刪,改,查4種情況,陣列也是一樣。

  ①.讀取元素

    對於陣列來說,讀取元素是最簡單的操作。由於陣列在記憶體中順序儲存,所以只要給出一個數組的下標,就可以讀到對應的陣列元素。

int[] array = new int[]{3,1,2,5,4,9,7,2};
//輸出陣列中下標為3的元素
System.out.println(array[3]);

  ②.更新元素

    要把陣列中的某一個元素的值替換為一個新值,也是非常簡單的操作,直接利用陣列下標就可以把新值賦給該元素。

int[] array = new int[]{3,1,2,5,4,9,7,2};
//給陣列下標為5的元素賦值 array[5] = 10; //輸出陣列中下標為5的元素 System.out.println(array[5]);

  ③.插入元素

    眾所周知,陣列的實際元素數量可能小於陣列的長度;因此,插入陣列元素的操作存在3種情況:

      • 尾部插入
      • 中間插入
      • 超範圍插入   

    a.尾部插入:是最簡單的情況,直接把插入的元素放在陣列尾部的空閒位置即可,等同於更新元素的操作。

    b.中間插入:稍微複雜一些。由於陣列的每一個元素都有其固定的下標,所以不得不首先把插入位置及後面的元素向後移動,騰出地方,再把要插入的元素放到對應的陣列位置上。   

/**
* @Author Jack丶WeTa * @Date 2020/7/22 10:31 * @Description 陣列插入元素-中間插入 */ public class MyArray { private int[] array; private int size; public MyArray(int capacity) { this.array = new int[capacity]; this.size = 0; } /** * 陣列插入元素 */ public void insert(int index, int element) { //判斷下表是否超出範圍 if (index < 0 || index > size) {//由於陣列中預設是順序排列,並且預設值為0,因此此處就預設按照陣列下標由低到高新增元素 throw new IndexOutOfBoundsException("超出陣列實際元素範圍!"); } //從右向左迴圈,將元素逐個向右挪一位 for (int i = size - 1; i >= index; i--) { array[i + 1] = array[i]; } //騰出的位置放置新元素 array[index] = element; size++; } /** * 輸出陣列 */ public void output(int arrayLength) { for (int i = 0; i < arrayLength; i++) { System.out.println(array[i]); } } public static void main(String[] args) { MyArray myArray = new MyArray(10); myArray.insert(0, 3); myArray.insert(1, 7); myArray.insert(2, 9); myArray.insert(3, 5); myArray.insert(1, 6); myArray.output(10); } }

  程式碼中的成員變數size是陣列實際元素的數量。如果插入元素在陣列尾部,傳入的下標引數index等於size;如果插入元素在陣列中間後頭部,則index小於size。如果傳入的下標引數index大於size或者小於0,則認為非法輸入,會直接丟擲異常。

  可是,如果陣列不斷插入新的元素,元素數量超過了陣列的最大長度,陣列豈不是要“撐爆”了,這時,就會有超範圍插入的概念。

    c.超範圍插入:假如現在又一個長度為6的陣列,已經裝滿了元素,這時還想插入一個新元素。這就涉及到陣列的擴容了。但是陣列在建立的時候已經確定了它的長度,這該如何做呢?此時,可以建立一個新的陣列,長度是舊陣列的2倍,再把舊陣列中的元素統統複製過去,這樣就實現了陣列的擴容。相關程式碼實現:

/**
 * @Author Jack丶WeTa
 * @Date 2020/7/22 11:22
 * @Description 程式碼中的成員變數size是陣列實際元素的數量。
 * 如果插入元素在陣列尾部,傳入的下標引數index等於size;
 * 如果插入元素在陣列中間後頭部,則index小於size。
 * 如果傳入的下標引數index大於size或者小於0,則認為非法輸入,會直接丟擲異常。
 */
public class MyArrayDilatation {
    private int[] array;
    //陣列實際元素數量
    private int size;

    public MyArrayDilatation(int capacity) {
        this.array = new int[capacity];
        this.size = 0;
    }

    /**
     * 陣列插入元素
     */
    public void insert(int index, int element) {
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException("超出陣列實際元素範圍!");
        }
        //如果實際元素達到陣列容量上限,則對陣列進行擴容
        if (size >= array.length) {
            resize();
        }
        //從右向左迴圈,將元素逐個向右挪一位
        for (int i = size - 1; i >= index; i--) {
            array[i + 1] = array[i];
        }
        //騰出的位置放置新元素
        array[index] = element;
        size++;
    }

    /**
     * 陣列擴容
     */
    public void resize() {
        int[] newArray = new int[array.length * 2];
        //從舊陣列複製到新陣列
        System.arraycopy(array, 0, newArray, 0, array.length);
        array = newArray;
    }

    /**
     * 輸出陣列
     */
    public void output(int arrayLength) {
        for (int i = 0; i < arrayLength; i++) {
            System.out.println(array[i]);
        }
    }

    /**
     *
     * @param index
     * @return deleteElement:被刪除的元素
     */
    public int delete(int index) {
       if (index < 0 || index >= size) {
           throw new IndexOutOfBoundsException("超出陣列實際元素範圍!");
       }
       int deleteElement = array[index];
       //從左向右迴圈,將元素逐個向左挪1位
       for (int i = index; i < size - 1; i++) {
           array[i] = array[i+1];
       }
       size--;
       return deleteElement;
    }

    public static void main(String[] args) {
        MyArrayDilatation myArrayDilatation = new MyArrayDilatation(4);
        myArrayDilatation.insert(0, 3);
        myArrayDilatation.insert(1, 7);
        myArrayDilatation.insert(2, 9);
        myArrayDilatation.insert(3, 5);
        myArrayDilatation.insert(1, 6);
        myArrayDilatation.output(4 * 2);
        int deleteElement = myArrayDilatation.delete(2);
        System.out.println("==================被刪掉的元素是:"+ deleteElement +"=====================");
        myArrayDilatation.output(4 * 2);
    }
}

  d.刪除元素:陣列的刪除操作和插入操作的過程相反,如果刪除的元素位於陣列中間,其後的元素都需要向前挪動1位。

    由於不涉及到擴容的問題,所以刪除程式碼實現比插入操作要簡單

  /**
     *
     * @param index
     * @return deleteElement:被刪除的元素
     */
    public int delete(int index) {
       if (index < 0 || index >= size) {
           throw new IndexOutOfBoundsException("超出陣列實際元素範圍!");
       }
       int deleteElement = array[index];
       //從左向右迴圈,將元素逐個向左挪1位
       for (int i = index; i < size - 1; i++) {
           array[i] = array[i+1];
       }
       size--;
       return deleteElement;
    }

4.時間複雜度分別為多少呢?

  ①.陣列讀取元素的時間複雜度為O(1);

  ②.陣列更新元素的時間複雜度為O(1);

  ③.陣列插入元素的操作包含兩部分:陣列擴容的時間複雜度O(n)和插入並移動元素的時間複雜度O(n),綜合起來,插入元素的時間複雜度為O(n);

  ④.陣列刪除元素的操作只涉及到元素的移動,所以時間複雜度也為O(n)。

5.對於刪除操作,其實還存在一種取巧的方式。我們都可以來思考一樣,如果要求只是刪除陣列中的一個元素,那麼就可以將要刪除的元素複製到最後一個元素的位置,然後刪掉最後一個元素。這樣一來,無需進行大量的資料移動,時間複雜度降為O(1)。但是這種方式只做參考,並不是刪除元素的主流的操作方式。

6.總結

  陣列這種資料結構有什麼優勢和劣勢呢?

  優勢:陣列擁有非常高效的隨機訪問能力,只要給出下標,就可以用常量時間找到對應元素。有一種高效查詢元素的演算法叫做二分查詢,就是利用了陣列的這個優勢。

  劣勢:至於陣列的劣勢,體現在插入和刪除元素方面。由於陣列元素連續緊密地儲存在記憶體中,插入和刪除元素都會導致大量元素被迫移動,影響效率。

  總的來說,陣列所適合的是讀操作多,寫操作少的場景。