1.定義
很多編程語言中數組的長度是固定的,就是定義數組的時候需要定義數組的長度,所以當數組已經被數據填滿的時候,需要再加入新的元素就很困難。只能說在部分變成語言中會有這種情況,在javascript中和php中數組的長度是可以任意增加的。在數組中添加和刪除元素也是比較麻煩,因為要將數組中其他元素向前或者向后平移,這個在javascript中也不是問題,javascript中有一個很方便的方法splice()方法很方便的就可以添加或刪除元素。
但是凡是都是相對的,javascript中的數組也有自己的問題,他們被是成了對象,與其他語言(比如c++和java)相比它的效率很低。
如果在實際的使用中發現數組的效率很慢,就可以考慮使用鏈表來代替。數組還有個優勢是可以根據鍵值很方便的訪問數組的值,除此之外,鏈表在任何場合都可以代替數組。如果需要隨機地訪問元素,數組仍然是更好的選擇。
鏈表是由一組節點組成的集合。每一個節點都使用一個對象的引用指向它的后續借點。指向另外一個借點的引用叫做鏈。
數組元素靠它們的位置進行引用,鏈表元素則是靠相互之間的關系進行引用。在數組中會說這個元素是數組中的第幾個元素,但是在鏈表中就說這個元素是某個元素的后面一個元素。遍歷鏈表就是跟着鏈表從鏈表的頭元素(head)一直走到尾元素(但是不包含鏈表的頭借點,頭通常用來作為鏈表的接入點)。還有一個問題,鏈表的尾元素指向一個null節點。如下圖1
圖1
許多鏈表的實現都在鏈表前面有一個特殊的節點,叫做頭節點。最后一個節點指向null,所有最后再加上一個null節點。如下圖2
圖2
在鏈表中插入一個節點的效率很高。向鏈表中插入一個節點,需要修改它前面的節點,使其指向新加入的節點,而新加入的節點則指向前面指向的節點。如下圖展示的是在eggs后面加上cookies節點。如下圖3.
圖3
從鏈表中刪除一個節點也很簡單,將待刪除的元素的前驅節點指向待刪除的后續節點,同時將待刪除元素指向null來釋放。下圖是一個巧合刪除是的null元素前面的一個元素。如下圖4
圖4
2.代碼實現
在下面的鏈表實現中有兩個類。node類用來標識節點,LinkedList類提供插入節點,刪除節點,顯示鏈表節點元素的方法,以及一些其他的輔助方法。
Node類包含兩個屬性,element用來保存節點上的數據,next用來保存指向下一個節點的鏈接。我們使用一個構造函數來創建節點,改構造函數設置了這兩個屬性的值。如下:
function Node(element){ this.element = element; this.next = null; }
LinkedList類提供了對鏈表進行操作的方法,該類的功能包含插入節點,在鏈表中查找給定的節點。該類也有一個構造函數,鏈表只有一個屬性,那就是使用一個node對象來保存該鏈表的頭結點,代碼如下:
function LList(){ this.head = new Node('head'); this.find = find; this.insert = insert; //this.remove = remove; this.display = display; }
head節點的next屬性被初始華為null,當有新元素插入時,next會指向新的元素。
插入節點的方法是insert,該方法向鏈表中插入新節點的時候,需要明確指出在那個節點的前面或者后面插入。這里先討論在一個已知節點的后面插入元素。在元素后面插入元素的時候不,需要先找到“后面”的節點。為此創建一個輔助方法find(),該方法遍歷鏈表,查找指定的數據,如果找到該數據,就返回保存該數據的節點,find()方法的實現的代碼如下:
function find(item){ var currNode = this.head; while (currNode.element != item){ currNode = currNode.next; } return currNode; }
find()方法演示了如何在鏈表上移動。首先創建一個新節點,並將鏈表的頭節點賦給這個新創建的節點。然后再鏈表上進行循環,如果當前節點的element屬性和我們要找的信息不符合,就從當前節點移動到下一個節點。如果查找成功個,該方法返回包含該數據的節點,否則返回null。
一旦找到“后面”的節點,就可以將新節點插入鏈表了。首先,將新節點的next屬性設置為“后面”節點對應的值,然后設置“后面”節點的next屬性指向新的節點,insert()定義如下:
//插入一個元素 function insert(newElement, item){ var newNode = new Node(newElement); var current = this.find(item); newNode.next = current.next; current.next = newNode; }
最后,我們定義一個打印鏈表元素的方法,如下:
function display(){ var currNode = this.head; while (!(currNode.next == null)){ document.write(currNode.next.element + ' '); currNode = currNode.next; } }
這個方法首先將鏈表的頭賦給一個變量,然后循環遍歷鏈表,當前節點的next屬性為null的時候循環結束。為了只顯示包含數據的節點,我們使用currNode.next.element表達式來訪問節點中的數據。
最后我們用下面的代碼來測試鏈表。在鏈表中保存幾個美國城市"Conway","Russellville","Alma"並將他們打印出來,完整代碼如下:
function Node(element){ this.element = element; this.next = null; } function LList(){ this.head = new Node('head'); this.find = find; this.insert = insert; //this.remove = remove; this.display = display; } 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 current = this.find(item); newNode.next = current.next; current.next = newNode; } function display(){ var currNode = this.head; while (!(currNode.next == null)){ document.write(currNode.next.element + ' '); currNode = currNode.next; } } //測試程序 var cities = new LList(); cities.insert("Conway", "head"); cities.insert("Russellville", "Conway"); cities.insert("Alma", "Russellville"); cities.display();
最后輸出的結果如下: