用JavaScript實現基於對象的單項鍊表
單向鏈表介紹
何爲單向鏈表
與數組的索引: 值
(可以直接在數組層面對值進行修改)不同,鏈表各個值的關係偏向去中心化。
見上圖,每個節點可分爲兩部分:1. 節點的值;2. 下一個節點的指向,即下一個節點是誰。例如:上圖鏈表中的第一個節點,它的值是milk,它的下一個節點是值爲Bread
的節點。
單向鏈表優點
當我們需要組織數據時,數組不總是最佳的數據結構(對於大多數編程語言而言),緣於它有兩個顯著的缺點:1. 數組的長度是固定的,當數組已被數據填滿時,加入新數據會顯得很麻煩;2. 當我們插入、或刪除數組中的某個值時,需要把數組中的其它值向後、或向前移動,以反映數組被進行了增刪操作。
所以當我們需要頻繁的對數據進行增刪操作時,選擇數組不是那麼地明智,這時我們可以使用鏈表來實現對數據的增刪功能。鏈表的增刪會方便很多,當增加數據時,只需要更改它的節點指向和上一級節點的節點指向;當刪除數據時,只需把待刪除節點的節點指向賦給上一級節點即可,此時待刪除節點便在鏈表中消失了(因爲無法通過節點與節點的關係再找到它,便可視爲消失了)。
例如:如果我們在milk和bread中增加sandwich數據,只需把milk的節點指向改爲sandwich,把指向bread的節點指向賦給sandwich即可;當需要刪除sandwich時,只需把指向bread的節點指向賦給milk即可,此時sandwich便自動消失了。
構建鏈表
當我們需要組織某一類的數據時,只需先建一個相應的構造器(構造函數)即可,這個構造器應包含以下的屬性和方法:頭節點;增,刪,查等。
節點構造器
我們可以先做頭節點的構造器;
//我們可以把一個節點視爲一個盒子,這個盒子被分爲兩部分,一部分盛放的是該節點的值,另一部分盛放的則是指向下一個節點的節點指向。
function Node(element) {
this.element = element;
this.next = null;
}
鏈表構造器
某類數據的鏈表構造器
function LList() { //LList: linkedList鏈表
//新建值爲head的節點(表頭),便可一生二二生三三生萬物了,下面的操作都要基於表頭。
this.head = new Node("head");
//查找方法,給find一個值,便可查到相應的節點
this.find = find;
//插入方法,可以實現在哪插入、插入什麼數據的功能
this.insert = insert;
//展示打印功能,傳入想要查詢的節點的元素,該節點便能被打印出來,包括的指向的下一節點
this.display = display;
//如果要刪除某一節點,則需要找到它的前一節點,修改它的節點指向
this.findPrevious = findPrevious;
//刪除方法,傳入想要刪除的元素,即可實現刪除功能
this.remove = remove;
}
find()
方法
find()
方法展示瞭如何在鏈表上進行移動。首先,新創一個節點,並將表頭賦給這個新創的節點。然後在鏈表上進行循環,如果如果當前節點的element屬性和我們要查找的值不一樣,就從當前節點移到下一節點,如果查找成功,返回包含該數據的節點,失敗,返回null
。
function find(item) {
//將表頭賦給新創的節點
var currNode = this.head;
//循環,如果當前節點的值與我們想要查找的值不一樣,移動到下一節點。知道找到目標或移到null
while (currNode.element !== item) {
currNode = currNode.next;
}
//返回相應節點或null
return currNode;
}
insert()
方法
insert()
實現插入功能,傳入新節點的值newElement
和待插位置item
,並修改新節點和待插位置的節點指向,完成插入。
function insert(newElement, item) {
var newNode = new Node(newElement);
var currNode = this.find(item);
newNode.next = currNode.next;
currNode.next = newNode;
}
display()
display()
顯示鏈表中的元素
function display() {
var currNOde = this.head;
while (currNode !== item) {
console.log(currNode.element);
currNode = currNode.next;
}
}
findPrevious()
findPrevious()
查找到待刪除的前一節點
function findPrevious(item) {
var currNode = this.head;
while ((currNode.next !== null) && (currNode.next.element !== item)) {
currNode = currNode;
}
return currNode;
}
remove()
方法
function remove(item) {
var preNode = this.findPrevious(item);
if(preNode.next !== null) {
preNode.next = preNode.next.next;
}
}
完整代碼
function Node(element) {
this.element = element;
this.next = null;
}
function LList() {
this.head = new Node("head");
this.find = find;
this.insert = insert;
this.display = display;
this.findPrevious = findPrevious;
this.remove = remove;
}
function find(item) {
var currNode = this.head;
while (currNode.element !== item) {
currNode = currNode.next;
}
return currNode;
}
function insert(newElement, item) {
var newNode = new Node(newElement);
var currNode = this.find(item);
newNode.next = currNode.next;
currNode.next = newNode;
}
function display() {
var currNode = this.head;
while (currNode.next !== null) {
console.log(currNode.next.element);
currNode = currNode.next;
}
}
function findPrevious(item) {
var currNode = this.head;
while ((currNode.next.element !== item) && (currNode.next !== null)) {
currNode = currNode.next;
}
return currNode;
}
function remove(item) {
var preNode = this.findPrevious(item);
if(preNode.next !== null) {
preNode.next = preNode.next.next;
}
}
測試
向鏈表中寫入數據
var cities = new LList();
cities.insert("beijing", "head");
cities.insert("shanghai", "beijing");
cities.insert("guangzhou", "shanghai");
cities.insert("shenzhen", "guangzhou");
展示鏈表
cities.display();
刪除"shanghai"
後,再次展示
cities.remove("shanghai");
cities.display();
刪除成功!
Reference
Michael McMillan, (2014). Data Structures & Algorithms with JavaScript. Beijing: O’Reilly Media, Inc. and Posts & Telecom Press.