資料結構(二)陣列
陣列就是把資料碼成一排進行存放:
陣列的最大優點:快速查詢。scores[2]
我們基於Java的靜態陣列,封裝一個屬於自己的動態陣列類Array,加深對於陣列這種資料結構的理解。
我們基於Java靜態陣列data來封裝我們的動態陣列Array類,capacity表示陣列容量,可以通過data.length獲得。size表示陣列元素個數,初始為0(也可以看成是下一個新增元素的索引位置)。
據此,設計Array類結構。
初始陣列類結構
public class Array<E> { private E[] data; private int size; // 建構函式,傳入陣列的容量captacity構造Array 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; } }
向陣列末尾新增元素
新增元素前:
新增元素後:
分析得出,如果要向陣列新增元素e,只要在size所在索引位置設定元素e,然後size向後移動(size++)即可。
據此,增加新增元素相關的程式碼:
// 在所有元素後新增一個新元素 public void addLast(E e) { if(size == data.length) { // 陣列容量已填滿,則不能再新增 throw new IllegalArgumentException("AddLast failed. Array is full."); } data[size] = e; size++; }
向指定位置新增元素
新增前:
新增後:
分析得出,只要把要插入元素的索引位置的當前元素以及其後的所有元素,都往後挪動一位,然後在指定索引位置設定新元素即可,最後size++。為避免元素覆蓋,具體挪的時候,需要從後往前推進完成元素挪動的整個過程。
修改程式碼:
// 在第index個位置插入一個新元素e public void add(int index, E 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++; }
調整addLast,複用add方法,同時增加一個addFirst:
// 在所有元素後新增一個新元素 public void addLast(E e) { add(size, e); } // 在所有元素前新增一個新元素 public void addFirst(E e) { add(0, e); }
獲取元素和修改元素
// 獲取index索引位置的元素 public E get(int index) { if (index < 0 || index >= size) { throw new IllegalArgumentException("Get failed. Index is illegal."); } return data[index]; } // 修改index索引位置的元素 public void set(int index, E e) { if (index < 0 || index >= size) { throw new IllegalArgumentException("Set 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; }
從陣列中刪除元素
刪除前:
刪除後:
分析得出,只要將要刪除位置之後的元素都往前挪動一位即可。然後size減1。
修改程式碼:
// 從陣列中刪除index位置的元素,返回刪除的元素 public E remove(int index) { if (index < 0 || index >= size) { throw new IllegalArgumentException("Remove failed. Index is illegal."); } E ret = data[index]; for (int i = index + 1; i < size; i++) { data[i-1] = data[i]; } size--; return ret; } // 從陣列中刪除第一個元素,返回刪除的元素 public E removeFirst() { return remove(0); } // 從陣列中刪除最後一個元素,返回刪除的元素 public E removeLast() { return remove(size - 1); } // 從陣列中刪除元素e(只刪除一個) public boolean removeElement(E e) { int index = find(e); if (index != -1) { remove(index); return true; } return false; }
動態陣列
容量開太大,浪費空間,容量開小了,空間不夠用。所以需要實現動態陣列。
具體做法如下:
就是在方法中開闢一個更大容量的陣列,迴圈遍歷原來的陣列元素,設定到新的陣列上。然後將原陣列的data指向新陣列。
修改程式碼:
// 陣列容量擴容/縮容 public void resize(int newCapacity) { E[] newData = (E[])new Object[newCapacity]; for (int i = 0; i < size; i++) { newData[i] = data[i]; } data = newData; }
修改新增元素的程式碼,新增時自動擴容:
// 在第index個位置插入一個新元素e public void add(int index, E e) { if (index < 0 || index > size) { throw new IllegalArgumentException("AddLast failed. Require index >= 0 and index <= size"); } if (size == data.length) { resize(2 * data.length); // 擴容為原來的2倍 } 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("Remove failed. Index is illegal."); } E ret = data[index]; for (int i = index + 1; i < size; i++) { data[i - 1] = data[i]; } size--; data[size] = null; // loitering objects != memory leak if (size == data.length / 4 && data.length / 2 != 0) { // 縮減陣列使用lazy方式(避免複雜度震盪),在1/4的時候縮容 resize(data.length / 2); // 縮容為原來的一半 } return ret; }
測試陣列
@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 main(String[] args) { Array<Integer> arr = new Array<>(); for (int i = 0; i < 10; i++) { arr.addLast(i); } System.out.println(arr); arr.add(1, 100); System.out.println(arr); arr.addFirst(-1); System.out.println(arr); arr.remove(2); System.out.println(arr); arr.removeElement(4); System.out.println(arr); arr.removeFirst(); System.out.println(arr); }
console輸出:
Array: size = 10, capacity = 10 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Array: size = 11, capacity = 20 [0, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9] Array: size = 12, capacity = 20 [-1, 0, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9] Array: size = 11, capacity = 20 [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Array: size = 10, capacity = 20 [-1, 0, 1, 2, 3, 5, 6, 7, 8, 9] Array: size = 9, capacity = 20 [0, 1, 2, 3, 5, 6, 7, 8, 9]
完整程式碼
public class Array<E> { private E[] data; private int size; // 建構函式,傳入陣列的容量captacity構造Array 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; } // 在第index個位置插入一個新元素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); // 擴容為原來的2倍 } for (int i = size - 1; i >= index; i--) { data[i + 1] = data[i]; } data[index] = e; size++; } // 在所有元素後新增一個新元素 public void addLast(E e) { add(size, e); } // 在所有元素前新增一個新元素 public void addFirst(E e) { add(0, e); } // 獲取index索引位置的元素 public E get(int index) { if (index < 0 || index >= size) { throw new IllegalArgumentException("Get failed. Index is illegal."); } return data[index]; } // 修改index索引位置的元素 public void set(int index, E e) { if (index < 0 || index >= size) { throw new IllegalArgumentException("Set 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("Remove failed. Index is illegal."); } E ret = data[index]; for (int i = index + 1; i < size; i++) { data[i - 1] = data[i]; } size--; data[size] = null; // loitering objects != memory leak if (size == data.length / 4 && data.length / 2 != 0) { // 縮減陣列使用lazy方式(避免複雜度震盪),在1/4的時候才縮容 resize(data.length / 2); // 縮容為原來的一半 } return ret; } // 從陣列中刪除第一個元素,返回刪除的元素 public E removeFirst() { return remove(0); } // 從陣列中刪除最後一個元素,返回刪除的元素 public E removeLast() { return remove(size - 1); } // 從陣列中刪除元素e(只刪除一個) public boolean removeElement(E e) { int index = find(e); if (index != -1) { remove(index); return true; } return false; } public void resize(int newCapacity) { E[] newData = (E[])new Object[newCapacity]; for (int i = 0; i < size; i++) { newData[i] = data[i]; } data = newData; } @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 main(String[] args) { Array<Integer> arr = new Array<>(); for (int i = 0; i < 10; i++) { arr.addLast(i); } System.out.println(arr); arr.add(1, 100); System.out.println(arr); arr.addFirst(-1); System.out.println(arr); arr.remove(2); System.out.println(arr); arr.removeElement(4); System.out.println(arr); arr.removeFirst(); System.out.println(arr); } }