第二十二篇 玩转数据结构——构建动态数组
- 數(shù)組就是把數(shù)據(jù)碼成一排進(jìn)行存放。
- Java中,數(shù)組的每個(gè)元素類(lèi)型必須相同,可以都為int類(lèi)型,string類(lèi)型,甚至是自定義類(lèi)型。
- 數(shù)組的命名要語(yǔ)義化,例如,如果數(shù)組用來(lái)存放學(xué)生的成績(jī),那么命名為scores就比較合適。
- 索引(index)是數(shù)組中的一個(gè)重要概念,它是我們給數(shù)組中的每個(gè)元素分配的編號(hào),從0開(kāi)始,依次遞增。如果數(shù)組中存放了n個(gè)元素,第一個(gè)元素的索引是0,最后一個(gè)元素的索引是n-1。
- 通過(guò)索引,我們可以對(duì)數(shù)組中的元素進(jìn)行快速訪(fǎng)問(wèn),例如,我們?cè)L問(wèn)索引為2的元素也就是數(shù)組中的第3個(gè)元素,就可以通過(guò)scores[2]這種形式。
- 在Java中聲明一個(gè)簡(jiǎn)單的數(shù)組
- public class Main {public static void main(String[] args) {int[] arr = new int[10];for (int i = 0; i < arr.length; i++)arr[i] = i;}
}
?
- 聲明一個(gè)有初始值的數(shù)組
- public class Main {public static void main(String[] args) {int[] scores = new int[]{100, 99, 86};for (int i = 0; i < scores.length; i++)System.out.println(scores[i]);}
}
?
- for循環(huán)的另一種使用形式
- public class Main {public static void main(String[] args) {int[] scores = new int[]{100, 99, 86};for (int score : scores)System.out.println(score);}
}
?
- 修改數(shù)組中的元素
- socres[1] = 98;
?
- 數(shù)組的索引可以是語(yǔ)義化的,也可以是非語(yǔ)義化的。
- 數(shù)組的最大優(yōu)點(diǎn),就是可以通過(guò)索引對(duì)數(shù)據(jù)進(jìn)行快速查詢(xún),因此,我們傾向于使用語(yǔ)義化的索引,這樣我們就很清楚自己在查什么。
- 如果我們的應(yīng)用場(chǎng)景中,索引沒(méi)有語(yǔ)義,那么使用其它數(shù)據(jù)結(jié)構(gòu)可能是更好的選擇。
- 對(duì)于一些特殊應(yīng)用場(chǎng)景,雖然使用了語(yǔ)義化索引,但依然不適合使用數(shù)組,例如,身份證號(hào),我們不能使用身份證號(hào)來(lái)作為數(shù)組的索引,因?yàn)檫@個(gè)數(shù)字太大了,會(huì)導(dǎo)致巨大的空間浪費(fèi)。
- 如果數(shù)組的索引是非語(yǔ)義化的,我們就需要考慮很多問(wèn)題,例如,當(dāng)數(shù)組的空間未被填滿(mǎn)時(shí),如何表示空位處的元素?如何向數(shù)組中添加新的元素?如何刪除掉數(shù)組中原有的元素?等等。Java所提供的原生數(shù)組是無(wú)法解決這些問(wèn)題的,我們需要定制屬于自己的數(shù)組類(lèi)Array,即,基于Java的原生數(shù)組,二次封裝屬于我們自己的數(shù)組類(lèi)。
2.. 實(shí)現(xiàn)自定義數(shù)組類(lèi)Array所包含的基本方法:
- public class Array {private int[] data; //設(shè)置為private,不希望用戶(hù)從外部直接獲取這些信息,防止用戶(hù)篡改數(shù)據(jù)private int size;//構(gòu)造函數(shù),傳入數(shù)組的容量capacity構(gòu)造Arraypublic Array(int capacity) {data = new int[capacity];size = 0;}//無(wú)參數(shù)構(gòu)造函數(shù),默認(rèn)數(shù)組容量capacity=10public Array() {this(10); //這里的capacity是IDE自動(dòng)添加的提示信息,實(shí)際不存在
}//獲取數(shù)組中的元素個(gè)數(shù)public int getSize() {return size;}//獲取數(shù)組的容量public int getCapacity() {return data.length;}//判斷數(shù)組是否為空public boolean isEmpty() {return size == 0;}
}
?
3.. 實(shí)現(xiàn)向自定義數(shù)組中添加元素的方法
- 向數(shù)組中添加元素的最簡(jiǎn)單的方法就是向數(shù)組的末尾添加元素
- //向數(shù)組末尾添加一個(gè)新元素
public void addLast(int e) {if (size == data.length) {throw new IllegalArgumentException("AddLast failed. Array is full.");}data[size] = e;size++;
}
?
- 向數(shù)組中指定索引位置插入一個(gè)元素
- //在index位置插入一個(gè)新元素e
public void add(int index, int 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++;
}
?
- 定義完向數(shù)組中指定索引位置插入一個(gè)元素的方法add之后,我們之前定義的向數(shù)組末尾插入元素的方法addLast其實(shí)可以調(diào)用add方法來(lái)實(shí)現(xiàn),我們調(diào)整addLast方法如下:
- //向數(shù)組末尾添加一個(gè)新元素
public void addLast(int e) {add(size, e);
}
?
- 我們還可以調(diào)用add方法實(shí)現(xiàn)一個(gè)向數(shù)組開(kāi)頭添加一個(gè)新元素的方法
- //向數(shù)組開(kāi)頭添加一個(gè)新元素
public void addFirst(int e) {add(0, e);
}
?
4.. 實(shí)現(xiàn)在數(shù)組中查詢(xún)?cè)睾托薷脑氐姆椒?/p>
- 實(shí)現(xiàn)自定義打印數(shù)組格式
- @Override
public String toString() { //覆蓋父類(lèi)的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();
}
?
- 在main函數(shù)中進(jìn)行測(cè)試:
- public class Main {public static void main(String[] args) {Array arr = new Array(20);for (int i = 0; i < 10; i++) {arr.addLast(i); //測(cè)試addLast方法
}System.out.println(arr);arr.add(1, 100); //測(cè)試add方法
System.out.println(arr);arr.addFirst(-1); //測(cè)試addFirst方法
System.out.println(arr);}}
?
- 打印效果如下:
- Array: size=10, capacity=20
[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]
?
- 實(shí)現(xiàn)獲取指定索引元素的方法
- //獲取index位置的元素
public int get(int index) {if (index < 0 || index >= size) {throw new IllegalArgumentException("Get failed. Index is illegal.");}return data[index];
}
?
- 實(shí)現(xiàn)修改指定索引元素的方法
- //修改index位置的元素為e
public void set(int index, int e) {if (index < 0 || index >= size) {throw new IllegalArgumentException("Set failed. Index is illegal.");}data[index] = e;
}
?
- 實(shí)現(xiàn)查看數(shù)組中是否包含元素e的方法
- //查找數(shù)組中是否存在元素e
public boolean contains(int e) {for (int i = 0; i < size; i++) {if (data[i] == e) {return true;}}return false;
}
?
- 實(shí)現(xiàn)查看數(shù)組中指定元素的索引的方法,若找不到,返回-1
- //查看數(shù)組中元素e的索引,若找不到元素e,返回-1
public int find(int e){for(int i=0;i<size;i++){if(data[i] == e){return i;}}return -1;
}
?
- 實(shí)現(xiàn)刪除數(shù)組中指定索引的元素的方法
- //刪除掉index位置的元素,并且返回所刪除的元素
public int remove(int index) {if (index < 0 || index >= size) {throw new IllegalArgumentException("Add failed. Require index >= 0 and index < size");}int ret = data[index];for (int i = index + 1; i < size; i++) {data[i - 1] = data[i];}size--;return ret;
}//刪除掉數(shù)組開(kāi)頭的元素,并返回所刪除的元素
public int removeFirst() {return remove(0);
}//刪除掉數(shù)組末尾的元素,并返回所刪除的元素
public int removeLast() {return remove(size - 1);
}//如果數(shù)組中有元素e,那么將其刪除,否則什么也不做
public void removeElement(int e) {int index = find(e);if (index != -1) {remove(index);}
}
?
5.. 整理我們目前實(shí)現(xiàn)的業(yè)務(wù)邏輯:
- public class Array {private int[] data; //設(shè)置為private,不希望用戶(hù)從外部直接獲取這些信息,防止用戶(hù)篡改數(shù)據(jù)private int size;//構(gòu)造函數(shù),傳入數(shù)組的容量capacity構(gòu)造Arraypublic Array(int capacity) {data = new int[capacity];size = 0;}//無(wú)參數(shù)構(gòu)造函數(shù),默認(rèn)數(shù)組容量capacity=10public Array() {this(10); //這里的capacity是IDE自動(dòng)添加的提示信息,實(shí)際不存在
}//獲取數(shù)組中的元素個(gè)數(shù)public int getSize() {return size;}//獲取數(shù)組的容量public int getCapacity() {return data.length;}//判斷數(shù)組是否為空public boolean isEmpty() {return size == 0;}//向數(shù)組末尾添加一個(gè)新元素epublic void addLast(int e) {add(size, e);}//向數(shù)組開(kāi)頭添加一個(gè)新元素epublic void addFirst(int e) {add(0, e);}//在index位置插入一個(gè)新元素epublic void add(int index, int 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++;}//獲取index位置的元素public int get(int index) {if (index < 0 || index >= size) {throw new IllegalArgumentException("Get failed. Index is illegal.");}return data[index];}//修改index位置的元素為epublic void set(int index, int e) {if (index < 0 || index >= size) {throw new IllegalArgumentException("Set failed. Index is illegal.");}data[index] = e;}//查找數(shù)組中是否存在元素epublic boolean contains(int e) {for (int i = 0; i < size; i++) {if (data[i] == e) {return true;}}return false;}//查看數(shù)組中元素e的索引,若找不到元素e,返回-1public int find(int e) {for (int i = 0; i < size; i++) {if (data[i] == e) {return i;}}return -1;}//刪除掉index位置的元素,并且返回刪除的元素public int remove(int index) {if (index < 0 || index >= size) {throw new IllegalArgumentException("Remove failed. Index is illegal.");}int ret = data[index];for (int i = index + 1; i < size; i++) {data[i - 1] = data[i];}size--;return ret;}//刪除掉數(shù)組開(kāi)頭的元素,并返回刪除的元素public int removeFirst() {return remove(0);}//刪除掉數(shù)組末尾的元素,并返回刪除的元素public int removeLast() {return remove(size - 1);}//如果數(shù)組中有元素e,那么將其刪除,否則什么也不做public void removeElement(int e) {int index = find(e);if (index != -1) {remove(index);}}@Overridepublic String toString() { //覆蓋父類(lèi)的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();}
}
?
6.. 現(xiàn)在我們的自定義數(shù)組的元素只允許為int類(lèi)型,我們需要進(jìn)行優(yōu)化,讓數(shù)組可以放置"任意"類(lèi)型的元素,解決這個(gè)問(wèn)題的技術(shù)稱(chēng)之為"泛型"。這里的"任意"加了引號(hào),這是因?yàn)樵贘ava中一個(gè)泛型類(lèi)并不能放置任意數(shù)據(jù)類(lèi)型的元素,泛型不能放置基本數(shù)據(jù)類(lèi)型,只能放置類(lèi)對(duì)象。在Java中,共有8中基本數(shù)據(jù)類(lèi)型:int、short、long、boolean、byte、char、float、double。如果將數(shù)組設(shè)置成泛型數(shù)組,那么就無(wú)法放置這些基本數(shù)據(jù)類(lèi)型了。為了解決這個(gè)問(wèn)題,Java語(yǔ)言為每個(gè)基本數(shù)據(jù)類(lèi)型都設(shè)計(jì)了一個(gè)包裝類(lèi),把本來(lái)不是類(lèi)對(duì)象的數(shù)據(jù)包裝成了類(lèi)對(duì)象。這8中基本數(shù)據(jù)類(lèi)型所對(duì)應(yīng)的包裝類(lèi)分別為:Int、Short、Long、Boolean、Byte、Char、Float、Double,在需要的情況下,包裝類(lèi)與其對(duì)應(yīng)的基本數(shù)據(jù)類(lèi)型可以自動(dòng)進(jìn)行轉(zhuǎn)換。
7.. 優(yōu)化后,Array類(lèi)的業(yè)務(wù)邏輯如下:- public class Array<E> {private E[] data; //設(shè)置為private,不希望用戶(hù)從外部直接獲取這些信息,防止用戶(hù)篡改數(shù)據(jù)private int size;//構(gòu)造函數(shù),傳入數(shù)組的容量capacity構(gòu)造Arraypublic Array(int capacity) {data = (E[]) new Object[capacity];size = 0;}//無(wú)參數(shù)構(gòu)造函數(shù),默認(rèn)數(shù)組容量capacity=10public Array() {this(10); //這里的capacity是IDE自動(dòng)添加的提示信息,實(shí)際不存在
}//獲取數(shù)組中的元素個(gè)數(shù)public int getSize() {return size;}//獲取數(shù)組的容量public int getCapacity() {return data.length;}//判斷數(shù)組是否為空public boolean isEmpty() {return size == 0;}//向數(shù)組末尾添加一個(gè)新元素epublic void addLast(E e) {add(size, e);}//向數(shù)組開(kāi)頭添加一個(gè)新元素epublic void addFirst(E e) {add(0, e);}//在index位置插入一個(gè)新元素epublic 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++;}//獲取index位置的元素public E get(int index) {if (index < 0 || index >= size) {throw new IllegalArgumentException("Get failed. Index is illegal.");}return data[index];}//修改index位置的元素為epublic void set(int index, E e) {if (index < 0 || index >= size) {throw new IllegalArgumentException("Set failed. Index is illegal.");}data[index] = e;}//查找數(shù)組中是否存在元素epublic boolean contains(E e) {for (int i = 0; i < size; i++) {if (data[i].equals(e)) {return true;}}return false;}//查看數(shù)組中元素e的索引,若找不到元素e,返回-1public 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]會(huì)指向一個(gè)類(lèi)對(duì)象,這部分空間不會(huì)被釋放loitering objectsdata[size] = null;return ret;}//刪除掉數(shù)組開(kāi)頭的元素,并返回刪除的元素public E removeFirst() {return remove(0);}//刪除掉數(shù)組末尾的元素,并返回刪除的元素public E removeLast() {return remove(size - 1);}//如果數(shù)組中有元素e,那么將其刪除,否則什么也不做public void removeElement(E e) {int index = find(e);if (index != -1) {remove(index);}}@Overridepublic String toString() { //覆蓋父類(lèi)的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();}
}
?
8.. 再次在main方法中實(shí)例化我們的自定義數(shù)組,進(jìn)行測(cè)試:
- public class Main {public static void main(String[] args) {Array<Integer> arr = new Array<>(20);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);arr.removeLast();System.out.println(arr);}
}
?
- 輸出結(jié)果如下:
- Array: size=10, capacity=20
[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]
Array: size=8, capacity=20
[0, 1, 2, 3, 5, 6, 7, 8]
?
9.. 測(cè)試讓自定義數(shù)組去承載對(duì)象
- public class Student {private String name;private int score;public Student(String studetName, int studentScore) {name = studetName;score = studentScore;}@Overridepublic String toString() {return String.format("Student(name: %s, score: %d)", name, score);}public static void main(String[] args) {Array<Student> arr = new Array<>();arr.addLast(new Student("XueZou", 98));arr.addLast(new Student("Guiche", 100));arr.addLast(new Student("QUiShui", 99));System.out.println(arr);}
}
?
- 輸出結(jié)果如下:
- Array: size=3, capacity=10
[Student(name: XueZou, score: 98), Student(name: Guiche, score: 100), Student(name: QUiShui, score: 99)]
?
- 修改add方法的業(yè)務(wù)邏輯,使自定義數(shù)組支持?jǐn)U容
- //在index位置插入一個(gè)新元素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 * size); //擴(kuò)大為原容量的2倍
}for (int i = size - 1; i >= index; i--) {data[i + 1] = data[i];}data[index] = e;size++;
}
?
- 我們需要實(shí)現(xiàn)resize方法,業(yè)務(wù)邏輯如下:
- private void resize(int newCapacity) {E[] newData = (E[]) new Object[newCapacity];for (int i = 0; i < size; i++) {newData[i] = data[i];}data = newData;
}
?
- 實(shí)現(xiàn)擴(kuò)容后,進(jìn)行測(cè)試:
- 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);
}
?
- 輸出效果如下:
- 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]
?
- 修改remove方法,使數(shù)組中的元素減少到一定程度時(shí),自動(dòng)縮小數(shù)組容量
- //刪除掉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]會(huì)指向一個(gè)類(lèi)對(duì)象,這部分空間不會(huì)被釋放loitering objectsdata[size] = null;if (size == data.length / 2) {resize(data.length / 2); //被利用的空間等于總空間的一半時(shí),將數(shù)組容量減少一半
}return ret;
}
?
- 實(shí)現(xiàn)自動(dòng)降低容量后,進(jìn)行測(cè)試:
- 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);
}
?
- 輸出效果如下:
- 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=10
[-1, 0, 1, 2, 3, 5, 6, 7, 8, 9]
Array: size=9, capacity=10
[0, 1, 2, 3, 5, 6, 7, 8, 9]
?
- O(1), O(n), O(lgn), O(nlgn), O(n^2)
- 大O描述的是算法的運(yùn)行時(shí)間和輸入數(shù)據(jù)之間的關(guān)系
- 通常我們會(huì)說(shuō)下面的這段代碼的算法是O(n)的,n在這里簡(jiǎn)單理解為nums中的元素個(gè)數(shù),O(n)是說(shuō)下面這段代碼的運(yùn)行效率,與nums中的元素個(gè)數(shù)n是呈線(xiàn)性關(guān)系的,線(xiàn)性關(guān)系體現(xiàn)在n是一次方。
- public static int sum(int[] nums) {int sum = 0;for (int num: nums) {sum += num;return sum;}
?
- 按照這個(gè)思路我們對(duì)上面所實(shí)現(xiàn)的方法,挨個(gè)分析一下它們的算法的時(shí)間復(fù)雜度:添加操作中addLast(e)方法的時(shí)間復(fù)雜度是O(1),這表示我們的算法所消耗的時(shí)間與我們數(shù)據(jù)的規(guī)模沒(méi)有關(guān)系,這里所指的數(shù)據(jù)規(guī)模是我們數(shù)組中的元素個(gè)數(shù);addFirst(e)方法的時(shí)間復(fù)雜度是O(n),因?yàn)槊總€(gè)元素都需要向后移動(dòng)一位;add(index, e)方法的時(shí)間復(fù)雜度與index的具體取值相關(guān),它的時(shí)間復(fù)雜度也是O(n);綜合來(lái)看,添加操作是一個(gè)O(n)級(jí)別的算法。
- 刪除操作中,removeLast(e)方法的時(shí)間復(fù)雜度是O(1);removeFirst(e)的時(shí)間復(fù)雜度是O(n);remove(index, e)的時(shí)間復(fù)雜度也是O(n);綜合來(lái)看,刪除操作的時(shí)間復(fù)雜度也是O(n)。
- 修改操作set(index, e)的時(shí)間復(fù)雜度是O(1),這是數(shù)組的最大優(yōu)勢(shì),專(zhuān)業(yè)術(shù)語(yǔ)稱(chēng)為"支持隨機(jī)訪(fǎng)問(wèn)"。
- 查詢(xún)操作中,get(index)方法的時(shí)間復(fù)雜度是O(1);find(e)方法和contains(e)方法的時(shí)間復(fù)雜度都是O(n)。
- 增:時(shí)間復(fù)雜度O(n);
- 刪:時(shí)間復(fù)雜度O(n);
- 改:已知索引O(1),未知索引O(n);
- 查:已知索引O(1),未知索引O(n);
- 因此,索引具有語(yǔ)義時(shí),選擇數(shù)組是非常好的,我們可以通過(guò)索引輕松檢索數(shù)據(jù)中的內(nèi)容。
- 對(duì)于添加操作,如果我們只使用addLast方法,那么它的時(shí)間復(fù)雜度本來(lái)應(yīng)該是O(1),但是,由于存在resize方法,而resize方法的時(shí)間復(fù)雜度是O(n),所以addLast方法的時(shí)間復(fù)雜度就不是O(1)了,但是它的均攤復(fù)雜度是O(1);
- 類(lèi)似的對(duì)于刪除操作,如果只使用removeLast方法,那么它的均攤復(fù)雜度也是O(1);
- 復(fù)雜度震蕩,是指我們?cè)跀?shù)組容量的臨界位置交替進(jìn)行添加和刪除操作,這會(huì)導(dǎo)致數(shù)組不斷執(zhí)行擴(kuò)容和縮容操作,而擴(kuò)容和縮容的時(shí)間復(fù)雜度都是O(n),出現(xiàn)這種問(wèn)題的原因在于,我們執(zhí)行removeLast方法后,resize得有點(diǎn)著急(Eager),解決方案是使用更加懶惰的策略(Lazy),簡(jiǎn)單理解就是,當(dāng)數(shù)組占用量剛好減到1/2時(shí),不著急縮容,等減到1/4時(shí),再觸發(fā)縮容,只縮1/2。
- 我們需要稍微改動(dòng)一下remove方法,如下所示:
- 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]會(huì)指向一個(gè)類(lèi)對(duì)象,這部分空間不會(huì)被釋放loitering objectsdata[size] = null;if (size == data.length / 4 && data.length / 2 != 0) { //改動(dòng)在這里resize(data.length / 2); //被利用的空間等于總空間的一半時(shí),將數(shù)組容量減少一半
}return ret;
}
?
轉(zhuǎn)載于:https://www.cnblogs.com/xuezou/p/9276945.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的第二十二篇 玩转数据结构——构建动态数组的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Word文件怎么快速查找关键词
- 下一篇: .net 技术类网址