01-手擼動態數組
本篇是戀上數據結構第一季個人總結
借鑒https://juejin.im/post/6844904001478066183#heading-0
本人git https://github.com/bigeyes-debug/Algorithm
一丶 數組
數組是一種順序存儲的線性表,所有元素的內存地址是連續的
由於數組的內存連續性,數組默認情況下(指靜態數組)是不會動態擴容的,而我們常常希望是數組是可以動態擴容的.
二丶動態數組的設計
2.1動態數組的屬性
- size是動態數組的大小,我們設置這個屬性,可以通過返回動態數組的大小,獲取當前動態數組元素的個數
- elements 存儲數據的數組(使用靜態數組保存,動態擴充代碼后面會提到)
- DEFAULT_CAPACITY 指定默認的動態數組的默認數組大小
- ELEMENT_NOT_FOUND 遍歷循環查找元素的時候,如果找不到元素,就返回-1
private int size;
private E[] elements;
private static final int DEFAULT_CAPACITY=10;
private static final int ELEMENT_NOT_FOUND=-1;
2.2 動態數組的接口
// 元素的數量
int size();
// 是否為空
boolean isEmpty();
// 是否包含某個元素
boolean contains(E element);
// 添加元素到最后面
void add(E element);
// 返回index位置對應的元素
E get(int index);
// 設置index位置的元素
E set(int index, E element);
// 往index位置添加元素
void add(int index, E element);
// 刪除index位置對應的元素
E remove(int index);
// 查看元素的位置
int indexOf(E element);
// 清除所有元素
void clear();
根據屬性和接口,我們可以直接實現一些接口的方法.
三丶動態數組的實現
3.1 數組的數量
size就是數組中元素的數量,調用size方法,直接返回數組中元素的數量
public int size() {
return size;
}
3.2 數組是否為空
public boolean isEmpty() {
return size==0;
}
3.3 添加元素
添加元素實現兩個接口
-
public void add(E element) 只添加元素
-
public void add(int index,E element) 指定添加元素的位置)
代碼如下:
public void add(int index, E element) {
elements[index] = element;
size++;
}
public void add(E element) {
add(size,element);
}
但添加元素存在兩種情況
第一種情況:在最后一個元素的后面添加新元素
第二種情況:將元素插入到某個位置(非最后面
第一種情況不需要處理,第二種情況我們需要插入數據的位置后面的元素逐個向前移
特別需要注意的是一定要從后往前移,如果從前往后,后面的元素會被覆蓋
考慮第二種情況之后,我們的add方法代碼如下
public void add(int index,E element) {
for(int i=size;i>index;i--) {
elements[i]=elements[i-1];
}
elements[index]=element;
size++;
}
3.3.1數組越界
如果add的index的值大於size,程序就會異常,index也應該大於,所以我們在此之前要加個判斷
private void rangeCheckForAdd(int index) {
if(index<0||index>size) {
outOfBounds(index);
}
public void add(int index,E element) {
rngeCheckForAdd(index);
for(int i=size;i>index;i--) {
elements[i]=elements[i-1];
}
elements[index]=element;
size++;
}
3.3.2數組擴容
-
由於數組elements最大的容量只有10,所以當數組存滿元素時,就需要對數組進行擴容。
-
因為數組是無法動態擴容的,所以需要創建一個新的數組,這個數組的容量要比之前數組的容量大。
-
然后在將原數組中的元素存放到新數組中,這樣就實現了數組的擴容。
private void ensureCapacity() {
// 獲取數組當前容量
int oldCapacity = elements.length;
// 如果 當前存儲的元素個數 < 當前數組容量, 直接返回
if (size < oldCapacity) return;
// 新數組的容量為原數組容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 創建新數組
E[] newElements = (E[]) new Object[newCapacity];
// 原數組中的元素存儲到新數組中
for (int i = 0; i < size; i++) {
newElements[i] = elements[i];
}
// 引用新數組
elements = newElements;
}
public void add(int index,E element) {
// index應該0<index<size
rangeCheckForAdd(index);
// 動態擴容
ensureCapacity(size+1);
//數組元素前移
for(int i=size;i>index;i--) {
elements[i]=elements[i-1];
}
//插入元素
elements[index]=element;
//數組數量+1
size++;
}
3.4 清空元素
在java中,當對象沒有指針指向時,就會自動被回收,所以我們只需要將數組的指向元素的指針設置為null,就可以了.
public void clear() {
for(int i=0;i<size;i++) {
elements[i]=null;
}
size=0;
}
3.5刪除元素
刪除元素的只需要將后面的元素往前移就可以,這里需要注意的是0<=index<size
private void outOfBounds(int index) {
throw new IndexOutOfBoundsException("Index:"+index+",Size:" + size);
}
private void rangeCheck(int index) {
if(index<0||index>=size) {
outOfBounds(index);
}
}
public E remove(int index) {
rangeCheck(index);
E old=elements[index];
//后面的元素前移
for(int i=index+1;i<size;i++) {
elements[i-1]=elements[i];
}
//一定要先減再清空
elements[--size]=null;
return old;
}
為什么要--size?
數組有5個元素.size為5,而第五個元素的下標是4
所有需要先--然后再設置為null
3.5修改元素
只需找到指定下標,替換就可,同樣要注意越界
public E set(int index,E element) {
rangeCheck(index);
E old = elements[index];
elements[index]=element;
return old;
}
3.6查找元素
查詢元素,只需要將指定索引的元素返回,注意索引是否越界即可。
public E get(int index) {
rangeCheck(index);
return elements[index];
}
3.7 查找元素位置
查找元素存在兩種情況,
第一種情況,要查找的元素為null,
第二種情況,要查找的元素不為null
null.equals回報空指針異常
我們分開進行處理,for循環遍歷即可
public int indexOf(E element) {
if(element == null) {
for(int i=0;i<size;i++) {
if(elements[i]==null) {
return i;
}
}
}
else {
for(int i=0;i<size;i++) {
if(element.equals(elements[i])) {
return i;
}
}
}
return ELEMENT_NOT_FOUND;
}
3.8 是否包含元素
public boolean contains(E element) {
return indexOf(element)!=ELEMENT_NOT_FOUND;
}