JavaScript 原生提供了數組類型,可是卻沒有鏈表,雖然日常的業務開發中,數組是能夠知足基本需求,可是鏈表在大數據集操做等特定的場景下明顯具備優點,那爲什麼 JavaScript 不提供鏈表類型呢?怎麼實現一個完整可用的鏈表呢?javascript
數組的特色前端
鏈表的特色java
talk is easy, show the code, 下面用 JavaScript 實現一個相對完整的單鏈表,並提供如下方法供調用:數組
function initList() {
class Node {
constructor(item) {
this.element = item
}
}
class List {
constructor() {
this.head = null
this.size = 0
this.last = null
}
/** * 鏈表查找元素 * @param index 查找的位置 */
find(index) {
let current = this.head
for (let i = 0; i < index; i++) {
current = current.next
}
return current
}
/** * 鏈表尾部插入元素 * @param element 插入的元素 */
push(element) {
let newNode = new Node(element)
if (this.size === 0) {
this.head = newNode
this.head.next = null
this.last = this.head
} else {
this.last.next = newNode
this.last = newNode
newNode.next = null
}
this.size += 1
}
/** * 鏈表尾部刪除元素 * @param element 刪除的位置 */
pop(element) {
this.last.next = null
}
/** * 鏈表頭部刪除元素 */
shift() {
if (this.size === 0)
return
this.head = this.head.next
if (this.size === 1)
this.last = null
this.size -= 1
}
/** * 鏈表頭部插入元素 * @param element 插入的元素 */
unshift(element) {
let newNode = new Node(element)
newNode.next = this.head
this.head = newNode
if (this.size === 0)
this.last = this.head
this.size += 1
}
/** * 鏈表插入元素 * @param element 插入的位置, index 插入的位置 */
insert(element, index) {
if (index < 0 || index > this.size) {
console.error('超出鏈表節點範圍')
return
}
let newNode = new Node(element)
if (this.size === 0) {
// 空鏈表
newNode.next = null
this.head = newNode
this.last = newNode
} else if (index === 0) {
// 插入頭部
newNode.next = this.head
this.head = newNode
} else if (index == this.size) {
//插入尾部
newNode.next = null
this.last.next = newNode
this.last = newNode
} else {
// 中間插入
let preNode = this.find(index - 1)
newNode.next = preNode.next
preNode.next = newNode
}
this.size += 1
}
/* *鏈表編輯元素 * @param element 編輯的元素,index 元素位置 */
edit(element, index) {
let current = this.find(index)
current.element = element
}
/* *鏈表刪除元素 * @param index 刪除元素位置 */
delete(index) {
let current = this.find(index)
if (index === 0) {
// 刪除頭節點
this.head = this.head.next
} else if (index === ((this.size) - 1)) {
// 刪除尾節點
let preNode = this.find(index - 1)
preNode.next = null
} else {
// 刪除中間節點
let preNode = this.find(index - 1)
let nextNode = preNode.next.next
let removeNode = preNode.next
preNode.next = nextNode
}
this.size -= 1
}
/* *鏈表使首尾成環 */
cycle() {
this.last.next = this.head
}
}
return new List()
}
let list = initList()
複製代碼
雙向鏈表的特色就是添加了指向上一個節點的指針(prev),比較單鏈表來講,稍微複雜一些,也更強大,這裏把上面的單鏈表修改一下。bash
function initList() {
class Node {
constructor(item) {
this.element = item
this.next = null
this.prev = null
}
}
class List {
constructor() {
this.head = null
this.size = 0
this.last = null
}
/**
* 鏈表查找元素
* @param index 查找的位置
*/
find(index) {
let current = this.head
for (let i = 0; i < index; i++) {
current = current.next
}
return current
}
/**
* 鏈表尾部插入元素
* @param element 插入的元素
*/
push(element) {
let newNode = new Node(element)
if (this.size === 0) {
this.head = newNode
this.head.next = null
this.last = this.head
} else {
this.last.next = newNode
newNode.next = null
newNode.prev = this.last
this.last = newNode
}
this.size += 1
}
/**
* 鏈表尾部刪除元素
* @param element 刪除的位置
*/
pop() {
if (this.size === 0)
return
if (this.size === 1) {
this.head = null
this.last = null
} else {
this.last.prev.next = null
this.last = this.last.prev
}
this.size -= 1
}
/**
* 鏈表頭部刪除元素
*/
shift() {
if (this.size === 0)
return
if (this.size === 1) {
this.head = null
this.last = null
} else {
this.head = this.head.next
this.head.prev = null
}
this.size -= 1
}
/**
* 鏈表頭部插入元素
* @param element 插入的元素
*/
unshift(element) {
let newNode = new Node(element)
if (this.size === 0) {
this.head = newNode
this.head.next = null
this.last = this.head
} else {
this.head.prev = newNode
newNode.next = this.head
this.head = newNode
}
this.size += 1
}
/**
* 鏈表插入元素
* @param element 插入的位置, index 插入的位置
*/
insert(element, index) {
if (index < 0 || index > this.size) {
console.error('超出鏈表節點範圍')
return
}
let newNode = new Node(element)
if (this.size === 0) {
// 空鏈表
this.head = newNode
this.head.next = null
this.last = this.head
} else if (index === 0) {
// 插入頭部
this.head.pre = newNode
newNode.next = this.head
this.head = newNode
} else if (index == this.size - 1) {
//插入尾部
newNode.next = null
newNode.prev = this.last
this.last.next = newNode
this.last = newNode
} else {
// 中間插入
let prevNode = this.find(index - 1)
newNode.next = prevNode.next
prevNode.next = newNode
newNode.prev = prevNode
newNode.next.prev = newNode
}
this.size += 1
}
/*
*鏈表編輯元素
* @param element 編輯的元素,index 元素位置
*/
edit(element, index) {
let current = this.find(index)
current.element = element
}
/*
*鏈表刪除元素
* @param index 刪除元素位置
*/
delete(index) {
let current = this.find(index)
if (index === 0) {
// 刪除頭節點
this.head = this.head.next
this.prev = null
} else if (index === ((this.size) - 1)) {
// 刪除尾節點
let preNode = this.find(index - 1)
preNode.next = null
} else {
// 刪除中間節點
let preNode = this.find(index - 1)
let nextNode = preNode.next.next
let removeNode = preNode.next
preNode.next = nextNode
nextNode.prev = preNode
}
this.size -= 1
}
/*
*鏈表使首尾成環
*/
cycle() {
this.last.next = this.head
this.head.prev = this.last
}
}
return new List()
}
let list = new initList()
複製代碼
循環鏈表能夠是單鏈表也能夠是雙向鏈表,它的特色是最後一個節點的 next 指針指向的是 head 節點 而不是 null,上面代碼已經提供了 cycle 方法來實現。框架
主要有這些方法:大數據
遍歷鏈表,使每個節點與以前節點比較,如有重複則爲有環鏈表優化
定義一個 Map 對象,遍歷鏈表到每個節點,若 Map 中沒有次節點 ID,則將節點 ID 爲 key, 存入 Map ,每一個節點判斷一次,若是某個節點的 ID存在,證實鏈表成環ui
雙指針法,舉個例子來講,兩我的在操場跑步,速度不一樣時,總會在某些時刻相遇,就是由於跑到是圓的(環),利用這個原理,定義一個循環和兩個指向頭節點的指針,一個每次移動一個節點,一個移動兩個節點,若是是成環的鏈表,某個時刻必然會遇到同一個節點。this
鏈表的特性代表其擅長修改,不擅查找,因此對於須要大量修改的操做,能夠考慮用鏈表實現,可是前端每每處理的數據量不會大,因此這種場景的實際意義不是很大,我的感受在框架的底層優化上,使用較多,業務開發中,數組夠用。
鏈表由於是隨機存儲的,因此比較省內存,可是對動態語言 JavaScript 解釋器來講,有自動的垃圾回收機制來管理內存,因此鏈表的這個優點就不明顯了。
鏈表特殊的結構,感受適合作輪播圖(雙向循環鏈表)、雙向導航列表等