1. 程式人生 > >資料結構之陣列詳解

資料結構之陣列詳解

陣列(Array)是由相同型別的元素(element)集合組成的固定長度(Size)的一種資料結構。在記憶體中是連續儲存的,因此可以通過索引(Index)計算出某個元素的地址。 下面介紹都是已java為示例。對於沒有詳細瞭解過的  相信有所收穫。  

基礎知識

宣告

type arrayName[] 或者 type[] arrayName。 如:
int arrInt[] 或者 int[] arrInt
宣告過程,只是告訴編譯器: arrInt變數儲存了一個int型別的陣列。這個過程並沒有分配記憶體。

new分配記憶體

分配記憶體需要通過new完成,同時指明型別和陣列大小。 如:
int[] arrInt = new int[4];
陣列中元素通過new分配後自動完成初始化,一般有幾種:數字型別初始化為0(如:int/byte/short/long初始化為0,float/double初始化為0.0),布林型初始化為false(boolean初始化為false),String或者其他物件型別初始化為null。 陣列賦值也有兩種
int[] arrInt = {1,3,5,7};
或
int[] arrInt = new int[4];
arrInt[0] = 1;
arrInt[1] = 3;
arrInt[2] = 5;
arrInt[3] = 7;

 示意圖如下:

多維陣列

多維陣列類似,即陣列中的元素也是陣列。如
int[][] arrInt = {{1,3,5},{2,4,6},{0,10,20}};

示意圖如下:

     

陣列特點

 

  1. 索引(即下標) 一般從0開始,如java, C/C++。
  2. 長度固定,在申請時長度固定。記憶體連續,在記憶體中則是申請一塊連續的固定大小的空間。
  3. 陣列有一維陣列和多維陣列,陣列元素可以是基本資料型別(Primitive),也可以是物件引用(Reference)。
  4. 隨機訪問,能夠根據位置(下標)直接訪問到元素。

 

注: 基本資料型別和物件引用資料型別 Primitive型別在java中有八種(具體內容後續整理),Primitive型別在記憶體位置直接存入實際的值,引用物件的記憶體分配是在堆上(Heap)。 Primitive型別: boolean、char、byte、short、int、long、float、double 陣列下標從0開始 陣列變數引用 指向了陣列實際分配的連續記憶體的開始地址 即第一個元素的地址(firstAdrr),陣列下標(index)即偏移量。假使每個元素大小為size. 那麼陣列元素位置計算:firstAdrr+index*size。 所以大部分語言陣列下標是0開始。如果從1開始,計算位置時偏移量要減1操作,多了一步運算。  

陣列關聯的Class物件

每個陣列有一個關聯的Class物件,與其他相同型別陣列共享。 如:JNI中傳遞的引數經常看到:I表示int型別,[ 表示一個數組,[I 即int陣列。
System.out.println("int array:" + new int[3].getClass());
System.out.println("int array:" + new int[3].getClass().getSuperclass());
System.out.println("String array:" + new String[3].getClass());
System.out.println("boolean array:" + new boolean[3].getClass());
結果為:
int array:class [I
int array super:class java.lang.Object
String array:class [Ljava.lang.String;
boolean array:class [Z

 

陣列的拷貝

一維陣列拷貝

一維陣列的clone()是 深度拷貝,拷貝了陣列內的原始資料到新陣列,而不是引用的複製。  如:
int arrInt[] = {1,2,3};
 
int arrClone[] = arrInt.clone();
arrClone[1] = 5;
  
System.out.println(arrInt == arrClone);
  
for (int i = 0; i < arrClone.length; i++) {
    System.out.println(arrInt[i]+" "+arrClone[i]);
}
結果為:
false
1 1
2 5
3 3

示意圖如下: 

多維陣列拷貝

但多維陣列則不是這樣。如:
int arrInt[][] = {{1,2,3},{4,5,6}};
        
int[][] arrClone = arrInt.clone();
arrClone[0][1] = 50;
arrInt[0][1] = 100;
  
System.out.println(arrInt == arrClone);
System.out.println(arrInt[0] == arrClone[0]);
  
for (int i = 0; i < arrInt.length; i++) {
    for (int j = 0; j < arrInt[i].length; j++) {
        System.out.println(arrInt[i][j]+" "+arrClone[i][j]);
    }
    
}
結果為:
false
true
1 1
100 100
3 3
4 4
5 5
6 6

 示意圖如下:

     

陣列操作

插入

如圖,向陣列中插入一個元素,插入位置及之後的所有元素都需要向後挪動一位來完成插入操作。 如果陣列已經滿了(或者保證陣列長度與元素個數一致),則只能建立一個新的陣列(長度增加的)來完成插入操作。 時間複雜度上,最好的是陣列未滿插入到最後 只需直接插入,操作一次(O(1))。最壞是插入到第一位,需要操作N次。 平均時間複雜度,(1 + 2 + ... + n) / n ~= O(n)   這是一個簡單的插入示例。在現有陣列的position位置插入value值,首先建立了一個新陣列(長度+1),依次填入資料。
private int[] insertArr(int[] srcArr, int position, int value) {
    if (position < 0 || position >= srcArr.length) return null;
    int[] desArr = new int[srcArr.length+1];
    System.arraycopy(srcArr, 0, desArr, 0, position);
    desArr[position] = value;
    System.arraycopy(srcArr, position, desArr, position+1, srcArr.length-position);
    return desArr;
}

 

刪除

類似插入的反向操作,刪除某個元素,該位置及之後的所有元素 向前挪一位。 如果要保證陣列長度與陣列元素個數一致,則也需要重新建立一個(長度減小的)陣列來完成。 示意圖:  

查詢

查詢某一元素位置,只能一位位的遍歷查找了。  

訪問

對於某位置的元素,直接讀取或修改 就可以了。陣列是隨機訪問的。  

時間複雜度總結

最後整理個表格,上述操作的時間複雜度大致如下,很容易理解就不詳細解釋了。

操作 平均時間複雜度 最壞條件時間複雜度
插入 O(n) O(n)
刪除 O(n) O(n)
查詢 O(n) O(n)
訪問 O(1) O(1)
  &nbs