數據結構之List | 讓我們一塊來學習數據結構


列表[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描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM