列表[List]的定義
列表是一組有序的數據。每個列表中的數據項稱為元素。在 JavaScript 中,列表中的元素 可以是任意數據類型。列表中可以保存多少元素並沒有事先限定,實際使用時元素的數量 受到程序內存的限制。
不包含任何元素的列表稱為空列表。列表中包含元素的個數稱為列表的 length
。在內部實 現上,用一個變量 listSize
保存列表中元素的個數。可以在列表末尾 append
一個元素, 也可以在一個給定元素后或列表的起始位置 insert
一個元素。使用 remove
方法從列表中 刪除元素,使用 clear
方法清空列表中所有的元素。
還可以使用 toString()
方法顯示列表中所有的元素,使用 getElement()
方法顯示當前元素。列表擁有描述元素位置的屬性。列表有前有后(分別對應 front 和 end)。使用 next()
方 法可以從當前元素移動到下一個元素,使用 prev()
方法可以移動到當前元素的前一個元 素。還可以使用 moveTo(n)
方法直接移動到指定位置,這里的 n 表示要移動到第 n 個位置。 currPos
屬性表示列表中的當前位置。
列表的抽象數據類型定義
屬性/方法 | 描述 |
---|---|
listSize | 列表的元素個數 |
pos | 列表的當前位置 |
length | 返回列表中元素的個數 |
clear(方法) | 清空列表中的所有元素 |
toString(方法) | 返回列表的字符串形式 |
getElement(方法) | 返回當前位置的元素 |
insert(方法) | 在現有元素后插入新元素 |
append(方法) | 在列表的末尾添加新元素 |
remove(方法) | 從列表中刪除元素 |
front(方法) | 將列表的當前位置設移動到第一個元素 |
end(方法) | 將列表的當前位置移動到最后一個元素 |
prev(方法) | 將當前位置后移一位 |
next(方法) | 將當前位置前移一位 |
currPos(方法) | 返回列表的當前位置 |
moveTo(方法) | 將當前位置移動到指定位置 |
contains(方法) | 判斷給定值是否在列表中 |
getElement(方法) | 顯示當前值 |
實現列表類
根據上面定義的列表抽象數據類型,可以直接實現一個 List 類。讓我們從定義構造函數開 始,雖然它本身並不是列表抽象數據類型定義的一部分:
class List {
constructor() {
this.dataSource = [];
this.listSize = 0;
this.pos = 0;
}
clear() {}
find() {}
toString() {}
insert() {}
append() {}
remove() {}
front() {}
end() {}
prev() {}
next() {}
length() {}
currPos() {}
moveTo() {}
getElement() {}
contains() {}
}
append:給列表添加元素
append(element) {
this.dataStore[this.listSize++] = element;
}
當新元素就位后,變量 listSize
加 1。
find:在列表中查找某一元素
find()
方法通過對數組對象 dataStore
進行迭代,查找給定的元素。如果找到,就返回該 元素在列表中的位置,否則返回 -1,這是在數組中找不到指定元素時返回的標准值。我們 可以在 remove() 方法中利用此值做錯誤校驗。
find(element) {
for (let i = 0, len = this.dataStore.length; i < len; ++i) {
if (this.dataStore[i] == element) {
return i;
}
}
return -1;
}
remove:從列表中刪除元素
remove()
方法是 List
類中較難實現的 一個方法。首先,需要在列表中找到該元素,然后刪除它,並且調整底層的數組對象以填 補刪除該元素后留下的空白。好消息是,可以使用數組的 splice()
方法簡化這一過程。
remove(element) {
const index = this.find(element);
if (index === -1) {
return false
}
this.dataStore.splice(index, 1);
--this.listSize;
return true;
}
length:列表中有多少個元素
length()
方法返回列表中元素的個數:
length() {
return this.listSize;
}
toString:顯示列表中的元素
現在是時候創建一個方法,用來顯示列表中的元素了。
toString() {
return this.dataStore.toString();
}
insert:向列表中插入一個元素
接下來要討論的方法是 insert()
。如果在List中間位置刪除了一個元素,但是現在又想將 它放回原來的位置,該怎么辦? insert()
方法需要知道將元素插入到什么位置,因此現在 我們假設插入是指插入到列表中某個元素之后。
insert(element, index) {
const index = this.find(element);
if (index === -1) {
return false;
}
this.dataStore.splice(index + 1, 0, element);
++this.listSize;
return true;
}
clear:清空列表中所有的元素
clear() {
delete this.dataStore;
this.dataStore = [];
this.listSize = this.pos = 0;
}
contains:判斷給定值是否在列表中
當需要判斷一個給定值是否在列表中時,contains()
方法就變得很有用。
contains(element) {
for (let i = 0, len = this.dataStore.length; i < len; ++i) {
if (this.dataStore[i] == element) {
return true;
}
}
return false;
}
也可以使用之前實現的find方法
遍歷列表
最后的一組方法允許用戶在列表上自由移動
front() {
this.pos = 0;
}
end() {
this.pos = this.listSize - 1;
}
prev() {
if (this.pos > 0) {
--this.pos;
}
}
next() {
if (this.pos < this.listSize - 1) {
++this.pos;
}
}
currPos() {
return this.pos;
}
moveTo(position) {
this.pos = position;
}
getElement() {
return this.dataStore[this.pos];
}
使用迭代器訪問列表
使用迭代器,可以不必關心數據的內部存儲方式,以實現對列表的遍歷。前面提到的方法 front()
、end()
、prev()
、next()
和 currPos
就實現了 List 類的一個迭代器。以下是和使 用數組索引的方式相比,使用迭代器的一些優點。
訪問列表元素時不必關心底層的數據存儲結構。
當為列表添加一個元素時,索引的值就不對了,此時只用更新列表,而不用更新迭代器。
可以用不同類型的數據存儲方式實現 List 類,迭代器為訪問列表里的元素提供了一種 統一的方式。 了解了這些優點后,來看一個使用迭代器遍歷列表的例子:
for(lists.front(); lists.currPos() < lists.length(); lists.next()) {
console.log(lists.getElement());
}
在 for 循環的一開始,將列表的當前位置設置為第一個元素。只要 currPos
的值小於列表 的長度,就一直循環,每一次循環都調用 next()
方法將當前位置向前移動一位。
同理,還可以從后向前遍歷列表,代碼如下:
for(lists.end(); lists.currPos() >= 0; lists.prev()) {
console.log(lists.getElement());
}
循環從列表的最后一個元素開始,當當前位置大於或等於 0 時,調用 prev()
方法后移 一位。
迭代器只是用來在列表上隨意移動,而不應該和任何為列表增加或刪除元素的方法一起 使用。
完整代碼
class List {
constructor() {
this.dataStore = [];
this.listSize = 0;
this.pos = 0;
}
clear() {
delete this.dataStore;
this.dataStore = [];
this.listSize = this.pos = 0;
}
find(element) {
for (let i = 0, len = this.dataStore.length; i < len; ++i) {
if (this.dataStore[i] == element) {
return i;
}
}
return -1;
}
toString() {
return this.dataStore.toString();
}
insert(element, index) {
const index = this.find(element);
if (index === -1) {
return false;
}
this.dataStore.splice(index + 1, 0, element);
++this.listSize;
return true;
}
append(element) {
this.dataStore[this.listSize++] = element;
}
remove(element) {
const index = this.find(element);
if (index === -1) {
return false;
}
this.dataStore.splice(index, 1);
--this.listSize;
return true;
}
length() {
return this.listSize;
}
contains(element) {
for (let i = 0, len = this.dataStore.length; i < len; ++i) {
if (this.dataStore[i] == element) {
return true;
}
}
return false;
}
front() {
this.pos = 0;
}
end() {
this.pos = this.listSize - 1;
}
prev() {
if (this.pos > 0) {
--this.pos;
}
}
next() {
if (this.pos < this.listSize - 1) {
++this.pos;
}
}
currPos() {
return this.pos;
}
moveTo(position) {
this.pos = position;
}
getElement() {
return this.dataStore[this.pos];
}
}
參考資料
- 數據結構與算法JavaScript描述