資料結構之陣列詳解
阿新 • • 發佈:2020-06-03
陣列(Array)是由相同型別的元素(element)集合組成的固定長度(Size)的一種資料結構。在記憶體中是連續儲存的,因此可以通過索引(Index)計算出某個元素的地址。
下面介紹都是已java為示例。對於沒有詳細瞭解過的 相信有所收穫。
每個陣列有一個關聯的Class物件,與其他相同型別陣列共享。
如:JNI中傳遞的引數經常看到:I表示int型別,[ 表示一個數組,[I 即int陣列。
結果為:
&nbs
基礎知識
宣告
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}};
示意圖如下:
陣列特點
- 索引(即下標) 一般從0開始,如java, C/C++。
- 長度固定,在申請時長度固定。記憶體連續,在記憶體中則是申請一塊連續的固定大小的空間。
- 陣列有一維陣列和多維陣列,陣列元素可以是基本資料型別(Primitive),也可以是物件引用(Reference)。
- 隨機訪問,能夠根據位置(下標)直接訪問到元素。
注: 基本資料型別和物件引用資料型別 Primitive型別在java中有八種(具體內容後續整理),Primitive型別在記憶體位置直接存入實際的值,引用物件的記憶體分配是在堆上(Heap)。 Primitive型別: boolean、char、byte、short、int、long、float、double 陣列下標從0開始 陣列變數引用 指向了陣列實際分配的連續記憶體的開始地址 即第一個元素的地址(firstAdrr),陣列下標(index)即偏移量。假使每個元素大小為size. 那麼陣列元素位置計算:firstAdrr+index*size。 所以大部分語言陣列下標是0開始。如果從1開始,計算位置時偏移量要減1操作,多了一步運算。
陣列關聯的Class物件
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) |