前面介紹了列表對數據排序,當時底層存儲數據的數據結構是數組。這裏將討論另一種列表:鏈表,它的底層存儲數據的數據結構是對象。javascript
數組不老是組織數據的最佳數據結構。由於java
若是數組在實際使用時很慢,就能夠考慮使用鏈表來替代它。除了對數據的隨機訪問,鏈表能夠用在任何能夠使用一維數組的狀況下。固然,若是須要隨機訪問,數組仍然是最優的選擇。c++
鏈表是由一組節點組成的集合。每一個節點均可以使用一個對象的引用指向它的後繼。指向另外一個節點的引用叫作:鏈。算法
在鏈表中插入一個節點的效率很高,向鏈表中插入一個節點,須要修改它前面的節點,使其指向新加入的節點,而新加入的節點則指向原來前驅指向的節點。編程
在鏈表中刪除一個節點也很快,將待刪除的元素的前驅節點指向待刪除元素的後繼節點,同時將待刪除元素指向null,元素就刪除成功了。數組
// Node類用來表示節點
function Node(element) {
this.element = element // element用來保存節點上的數據
this.next = null // next用來保存指向下一個節點的連接
}
// LinkedList類提供對鏈表進行操做的方法
function LinkedList() {
this.head = new Node('head')
}
LinkedList.prototype = {
constructor: LinkedList,
// 查找節點(遍歷鏈表,查找給定數據)
find: function (item) {
var currNode = this.head
while (currNode.element !== item) {
currNode = currNode.next
}
return currNode
},
// 插入節點
insert: function (newElement, item) {
var newNode = new Node(newElement)
var current = this.find(item) // 找到插入節點的位置
newNode.next = current.next // 將新節點的next屬性設置未‘後’節點的next屬性對應的值
current.next = newNode // 設置‘後’節點的next屬性指向新節點
},
// 查找要刪除節點的前一個節點
findPrev: function (item) {
var currNode = this.head
while (!(currNode.next === null) && (currNode.next.element !== item)) {
currNode = currNode.next
}
return currNode
},
// 刪除節點
remove: function (item) {
var prevNode = this.findPrev(item)
if (!(prevNode.next === null)) {
prevNode.next = prevNode.next.next // 讓前一個節點指向了待刪除節點的後一個節點
}
},
// 顯示鏈表中的元素
dispaly: function () {
var currNode = this.head
while (!(currNode.next === null)) {
console.log(currNode.next.element)
currNode = currNode.next
}
}
}
// 使用
var people = new LinkedList()
people.insert('wu', 'head')
people.insert('qin', 'wu')
people.insert('hao', 'qin')
people.insert('is', 'hao')
people.insert('a', 'is')
people.insert('good', 'a')
people.insert('boy', 'good')
people.dispaly()
console.log('-------------------------------------')
people.remove('good')
people.dispaly()
複製代碼
若是算法中須要頻繁地找某結點的前趨結點,單鏈表的解決方式是遍歷整個鏈表,增長算法的時間複雜度,影響總體效率。數據結構
爲了快速便捷地解決這類問題,在單向鏈表的基礎上,給各個結點額外配備一個指針變量,用於指向每一個結點的直接前趨元素。這樣的鏈表被稱爲「雙向鏈表」或者「雙鏈表」。編程語言
雙向鏈表中的結點有兩個指針域,一個指向直接前趨,一個指向直接後繼。(鏈表中第一個結點的前趨結點爲NULL,最後一個結點的後繼結點爲NULL)ui
// Node類用來表示節點
function Node(element) {
this.element = element // element用來保存節點上的數據
this.next = null // next用來保存指向下一個節點的連接
this.previous = null // previous用來保存指向上一個節點的連接
}
// LinkedListBoth類提供對雙向鏈表進行操做的方法
function LinkedListBoth() {
this.head = new Node('head')
}
LinkedListBoth.prototype = {
constructor: LinkedListBoth,
// 查找節點(遍歷鏈表,查找給定數據)
find: function (item) {
var currNode = this.head
while (currNode.element !== item) {
currNode = currNode.next
}
return currNode
},
// 插入節點
insert: function (newElement, item) {
var newNode = new Node(newElement)
var current = this.find(item) // 找到插入節點的位置
newNode.next = current.next // 將新節點的next屬性設置未‘後’節點的next屬性對應的值
newNode.previous = current
current.next = newNode // 設置‘後’節點的next屬性指向新節點
},
// 查找要刪除節點的前一個節點
//findPrev: function (item) {
// var currNode = this.head
// while (!(currNode.next === null) && (currNode.next.element !== item)) {
// currNode = currNode.next
// }
// return currNode
//},
// 刪除節點
remove: function (item) {
var currNode = this.find(item)
if (!(currNode.next === null)) {
currNode.previous.next = currNode.next
currNode.next.previous = currNode.previous
currNode.next = null
currNode.previous = null
}
},
// 顯示鏈表中的元素
dispaly: function () {
var currNode = this.head
while (!(currNode.next === null)) {
console.log(currNode.next.element)
currNode = currNode.next
}
},
// 查找最後節點
findLast: function () {
var currNode = this.head
while (!(currNode.next === null)) {
currNode = currNode.next
}
return currNode
},
// 反序顯示雙向鏈表中的元素
dispReverse: function () {
var currNode = this.head
currNode = this.findLast()
while (!(currNode.previous === null)) {
console.log(currNode.element)
currNode = currNode.previous
}
}
}
// 使用
var people = new LinkedListBoth()
people.insert('wu', 'head')
people.insert('qin', 'wu')
people.insert('hao', 'qin')
people.insert('is', 'hao')
people.insert('a', 'is')
people.insert('good', 'a')
people.insert('boy', 'good')
people.dispaly()
console.log('-------------------------------------')
people.remove('good')
people.dispaly()
console.log('-------------------------------------')
people.dispReverse()
複製代碼
若是你但願能夠從後向前遍歷列表,可是又不想付出額外表明來建立一個雙向列表,那麼就須要使用循環鏈表。從循環鏈表的尾節點向後移動,就等於從後向前遍歷鏈表。this