一邊學習前端,一邊經過博客的形式本身總結一些東西,固然也但願幫助一些和我同樣開始學前端的小夥伴。前端
若是出現錯誤,請在評論中指出,我也好本身糾正本身的錯誤node
author: thomaszhoubash
鏈表定義:app
function LinkedList() {
let Node = function(element) { // 輔助類,表示要添加到鏈表中的項
this.element = element;
this.next = null; //next屬性是隻想鏈表的下一個節點的指針
};
let length = 0,
head = null; // 頭指針
this.append = function(element) {}; // 向鏈表尾部添加一個新的項
this.insert = function(element, position) {}; // 向指定位置插入一個新的項
this.removeAt = function(position) {}; // 移除鏈表中的指定項
this.remove = function(element) {}; // 移除值爲element的項
this.indexOf = function(element) {}; // 返回元素在鏈表中的索引,若是沒有則返回-1
this.isEmpty = function() {}; // 判斷鏈表中是否爲空
this.size = function() {}; // 鏈表的長度
this.clear = function() {}; // 清空鏈表
this.print = function() {}; //輸出鏈表的值
}
複製代碼
this.append = function(element) { //向鏈表尾部添加一個新的項
let node = new Node(element),
current;
if (head === null) { // 鏈表中的第一個節點
head = node;
} else {
current = head;
while (current.next) { // 循環鏈表,直到找到最後一項
current = current.next;
}
current.next = node; // 找到最後一項,將next賦值爲node,創建鏈接
}
length += 1; //更新鏈表長度
};
複製代碼
this.print = function() {
let current = head;
for (let i = 0; i < length; i++) {
console.log(`第${i+1}個值:${current.element}`);
current = current.next;
}
};
複製代碼
向指定位置插入一個新的項,步驟以下:函數
this.insert = function(element, position) { // 向指定位置插入一個新的項
if (position >= 0 && position <= length) { // 檢查越界值
let node = new Node(element),
current = head, // 設置兩指針
previous;
if (position === 0) { // 在第一個位置添加
node.next = head;
head = node;
}else {
for (let i = 0; i < position; i++) {
// 使得current定在要插入的位置,previous定在要插入位置的前一個位置
previous = current;
current = current.next;
}
// 將新項node插入進去
node.next = current;
previous.next = node;
}
}else {
return 0;
}
length += 1;
};
複製代碼
對於函數參數含有position這類的位置參數,必定要注意越界判斷學習
removeAt的思想和前面的insert方法很類似,惟一的不一樣就是移除指定項時,改變指針指向的代碼不一樣previous.next = current.next;
,測試
previous.next = current.next;
和insert方法不一樣,其餘思想和作法都同樣this.removeaAt = function(position) { // 移除鏈表中的指定項
if (position >= 0 && position <= length) {
let current = head,
previous;
if (position === 0) {
head = head.next;
}else {
for (let i = 0; i < position; i++) { //此處循環到current指向要移除的位置,同insert方法
previous = current;
current = current.next;
}
// 經過將previous的next變化到current.next,將指定項移除
previous.next = current.next;
}
}else {
return 0;
}
length -= 1;
};
複製代碼
this.indexOf = function(element) { // 返回元素在鏈表中的索引,若是沒有則返回-1
let current = head; //
for (let i = 0; i < length; i++) {
if (current.element === element) {
return i;
}
current = current.next;
}
return -1;
};
複製代碼
this.remove = function(element) { // 移除值爲element的項
// remove方法能夠直接經過複用 this.indexOf(element) 和 this.removeAt(position) 方法實現
let index = this.indexOf(element);
return this.removeaAt(index);
};
複製代碼
this.isEmpty = function() { // 判斷鏈表中是否爲空
return length === 0;
};
this.size = function() { // 鏈表的長度
return length;
};
this.clear = function() { // 清空鏈表
let current = head;
for (let i = 0; i < length; i++) {
this.removeAt(i);
}
length = 0;
head = null;
};
複製代碼
(function LinkedListTest() {
let linked = new LinkedList();
linked.append(2);
linked.append(3);
linked.append(4);
linked.print(); // 第1個值:2 第2個值:3 第3個值:4
linked.insert(5, 1); // 位置從0開始
linked.print(); // 第1個值:2 第2個值:5 第3個值:3 第4個值:4
linked.removeAt(1); // 至關於將上面插入的5再刪除
linked.print(); // 第1個值:2 第2個值:3 第3個值:4
console.log(linked.indexOf(3)); // 1
console.log(linked.indexOf(10)); // -1
console.log(linked.size()); // 3
console.log(linked.isEmpty()); // false
linked.clear();
console.log(linked.isEmpty()); // true
})();
複製代碼
定義雙向鏈表
function DoublyLinkedList() {
let Node = function(element) {
this.element = element;
this.next = null;
this.prev = null; // 指向前一項的指針
};
let length = 0,
head = null,
tail = null; // 尾指針
//此處就是方法,都在下面詳細說明
}
複製代碼
相比較於單向鏈表,雙向鏈表比較複雜,它有後驅指針next還有前驅指針prev,那插入元素就要考慮的狀況也多了ui
insert思路:this
此處還有一個不同的地方,就是多了一個函數find,因爲雙向鏈表,咱們時能夠經過一個指針current來找到前驅結點和後驅結點,(單向鏈表只能訪問後驅結點,因此才須要previous指針來做爲前驅結點的指針)因此咱們在這裏取消了previous指針,而後根據前面的學習,咱們能夠發現有一部分代碼在插入和移除方法中都有,因此咱們將這段代碼抽離出來建立爲find方法spa
<script>
this.find = function(position) { // 遍歷到position位置
let current = head;
for (let i = 0; i < position; i++) {
current = current.next;
}
return current;
}
this.insert = function(element, position) { // 指定位置插入值
if (position >= 0 && position <= length) {
let node = new Node(element);
let current = head,
previous = null;
if (position === 0) { // case1: 從鏈表頭部插入
if (!head) {
// 鏈表爲空
head = node;
tail = node;
}else {
// 鏈表不爲空
node.next = head;
head.prev = node;
head = node;
}
}else if (position === length) { // case2: 從鏈表尾部插入
node.prev = tail;
tail.next = node;
tail = node;
}else { // case3: 從鏈表的中間插入
current = this.find(position);
// 插入元素
node.next = current;
node.prev = current.prev;
current.prev.next = node;
current.prev = node;
}
length += 1;
}else {
return 0;
}
};
複製代碼
思路同insert,分三個階段進行移除項的操做,我重點說一下中間移除項的操做,有些地方是經過current指針和previous指針一塊兒來遍歷,而後進行操做,其實previous能夠用current.prev來代替,由於這是雙向鏈表,因此徹底不須要多加一個previous指針。
/*
removeAt思路解析:
同insert,分三個階段進行移除項的操做
*/
this.removeAt = function(position) {
if (position >= 0 && position < length) {
let node = new Node(),
current = head;
if (position === 0) { // 移除第一項
head = head.next;
head.prev = null;
if (length === 1) { // 鏈表只有一項
tail = null;
}
}else if (position === length - 1) { // 移除最後一項
tail = tail.prev;
tail.next = null
}else { // 移除中間的項
current = this.find(position);
current.prev.next = current.next;
current.next.prev = current.prev;
}
length -= 1;
return current.element; // 返回被移除的項
}else {
return null;
}
};
複製代碼
關於其餘的方法,我只列出部分,由於其實單向和雙向的主要區別就在於有一個prev的前驅指針,還有一個尾指針tail,在其餘的方法中,咱們只須要注意這兩個點就好,如下代碼均有註釋
this.append = function(element) { // 尾部添加項
let node = new Node(element),
current = head;
if (head === null) {
head = node;
tail = node;
}else {
// 這裏是和單鏈表不一樣的地方,也就是添加。
tail.next = node;
node.prev = tail;
tail = node;
}
length += 1;
};
複製代碼
this.print = function() { // 輸出鏈表的值-同單向鏈表
let current = head;
for (let i = 0; i < length; i++) {
console.log(`第${i+1}個值:${current.element}`);
current = current.next;
}
};
this.clear = function() { // 清空鏈表
let current = head;
for (let i = 0; i < length; i++) {
this.removeAt(i);
}
length = 0;
head = null;
tail = null; // 此處須要將tail指針也賦值爲null
};
this.size = function() { // 鏈表的長度-同單向鏈表
return length;
};
複製代碼
關於循環鏈表,和單向鏈表和雙向鏈表最大的不一樣就是尾部的next指向head