鏈表
鏈表存儲有序的元素集合,不同於數組,鏈表中的元素在內存中並不是連續放置的。每個元素由一個存儲元素本身的節點和一個指向下一個元素的引用(也稱指針或鏈接)組成。如下圖:
要想訪問鏈表中間的一個元素,需要從起點(表頭)開始迭代鏈表直到找到所需的元素。
1、創建鏈表類:
/**
* append(element):向列表尾部添加一個新的項。
* insert(position, element):向列表的特定位置插入一個新的項。
* remove(element):從列表中移除一項。
* indexOf(element):返回元素在列表中的索引。如果列表中沒有該元素則返回-1。
* removeAt(position):從列表的特定位置移除一項。
* isEmpty():如果鏈表中不包含任何元素,返回true,如果鏈表長度大於0則返回false。
* size():返回鏈表包含的元素個數。與數組的length屬性類似。
* toString():由於列表項使用了Node類,就需要重寫繼承自JavaScript對象默認的toString方法,讓其只輸出元素的值。
* getHead():如果我們需要在類的外部實現循環訪問列表,就需要提供一種獲取類的第一個元素的方法。
*/
function LinkedList() {
// Node類表示要加入列表的項,
let Node = function(element) {
this.element = element; // 添加到列表的值
this.next = null; // 指向列表中下一個節點項的指針。
};
let length = 0; // 有存儲列表項的數量
let head = null; // 存儲第一個節點的引用
/**
* 向LinkedList對象尾部添加一個元素時,可能有兩種場景:
* 1、列表爲空,添加的是第一個元素;
* 2、列表不爲空,向其追加元素。
*/
this.append = function(element) {
let node = new Node(element);
let current;
if (head === null) {
head = node;
} else {
// 從第一項開始查找
current = head;
// 循環列表找到最後一項
while (current.next) {
current = current.next;
}
// 將最後一項的next指針指向新添加的node節點
current.next = node;
// 長度加1
length++;
}
};
this.insert = function(position, element) {
// 判斷邊界值
if (position >= 0 && position <= length) {
let node = new Node(element);
let current = head;
let previous;
let index = 0;
if (position === 0) {
node.next = current;
head = node;
} else {
while (index++ < position) {
previous = current;
current = current.next;
}
node.next = current;
previous.next = node;
}
length++;
return true;
} else {
return false;
}
};
this.removeAt = function(position) {
// 檢查邊界值
if (position > -1 && position < length) {
let current = head; // 要移除的元素
let previous; // 要移除元素的前一個元素
let index = 0;
if (position === 0) {
head = current.next;
} else {
while (index++ < position) {
previous = current;
current = current.next;
}
previous.next = current.next;
}
lenth--;
return current.element;
} else {
return null;
}
};
this.remove = function(element){
var index = this.indexOf(element);
return this.removeAt(index);
};
this.indexOf = function(element) {
let current = head;
let index = 0;
while (current) {
if (element === current.element) {
return index;
}
index++;
current = current.next;
}
return -1;
};
this.isEmpty = function() {
return length === 0;
};
this.size = function() {
return length;
};
this.toString = function() {
let current = head;
let string = '';
while (current) {
string = current.element + ',';
current = current.next;
}
let length = string.length;
if (string.lastIndexOf(',') === length - 1) {
string = string.substring(0, length - 1);
}
return string;
};
this.getHead = function(){
return head;
};
}
// 實例化鏈表類
var list = new LinkedList();
2、向鏈表尾部追加(append)元素的圖示:
list.append(15);
list.append(10);
3、從鏈表中移除(removeAt)元素的圖示:
list.removeAt(0)
list.removeAt(3)
list.removeAt(1)
4、向鏈表中任意位置添加元素的圖示:
list.insert(0,14)
list.insert(3,16)
list.insert(2,18)
雙向鏈表
雙向鏈表和普通鏈表的區別在於,在鏈表中,一個節點只有鏈向下一個節點的鏈接,而在雙向鏈表中,鏈接是雙向的:一個鏈向下一個元素,另一個鏈向前一個元素,如下圖:
1、創建雙向鏈表類:
/**
* insert(position, element):向列表的特定位置插入一個新的項。
* removeAt(position):從列表的特定位置移除一項。
*/
function DoublyLinkedList() {
let Node = function(element) {
this.element = element;
this.next = null;
this.prev = null;
};
let length = 0;
let head = null;
let tail = null;
this.insert = function(position, element) {
// 判斷邊界值
if (position >= 0 && position <= length) {
let node = new Node(element);
let current = head;
let previous;
let index = 0;
if (position === 0) {
if (!head) {
head = node;
tail = node;
} else {
node.next = current;
current.prev = node;
head = node;
}
} else if (position === length) {
current = tail;
current.next = node;
node.prev = current;
tail = node;
} else {
while (index++ < position) {
previous = current;
current = current.next;
}
previous.next = node;
node.next = current;
current.prev = node;
node.prev = previous;
}
length++;
return true;
} else {
return false;
}
};
this.removeAt = function(position) {
// 檢查邊界值
if (position > -1 && position < length) {
let current = head; // 要移除的元素
let previous; // 要移除元素的前一個元素
let index = 0;
if (position === 0) {
head = current.next;
if (length === 1) {
tail = null;
} else {
head.prev = null;
}
} else if (position === length - 1) {
current = tail;
tail = current.prev;
tail.next = null;
} else {
while (index++ < position) {
previous = current;
current = current.next;
}
// 刪除current
previous.next = current.next;
current.next.prev = previous;
}
lenth--;
return current.element;
} else {
return null;
}
};
}
2、在任意位置插入元素圖示:
在頭部插入:
在尾部插入:
在中間插入:
3、在任意位置刪除元素:
刪除頭部元素:
刪除尾部元素:
刪除中間元素:
循環鏈表
循環鏈表可以像鏈表一樣只有單向引用,也可以像雙向鏈表一樣有雙向引用。循環鏈表和鏈表之間唯一的區別在於,最後一個元素指向下一個元素的指針(tail.next)不是引用null,而是指向第一個元素(head),如下圖所示。
單向循環鏈表:
雙向循環鏈表:
循環鏈表暫無介紹。