1. 定義
在棧和佇列中,儲存物件都是通過陣列來實現的。但陣列也有一個缺點:陣列是定長
的。如果從素組的起點或中間插入或移除資料,那麼成本會很高(表面看不出來,但是實際是Array類方法在背後幫我們進行移動了)。
而連結串列儲存有序的資料結構,不同於陣列,連結串列的元素在記憶體中不需要連續放置,而是將相關的兩個元素a、b,通過a節點的指標指向b節點的地址來進行引用。
連結串列好處在於新增元素,不需要移動元素,只需要將插入位置的前後元素的指標修改即可。
當移除元素也是一樣的,將需要移除元素的前後節點 關聯起來
2. linkList例項
連結串列是一個類,它包含了以下的方法屬性:
- 節點node是一個單獨的物件,它應該擁有自己的屬性,不可缺少的是
next屬性
:指向下一個節點。 - append(element): 向連結串列中新增node,
新增到連結串列的尾巴上,但可能存在連結串列為空的情況
- insert(position, element): 向指定位置插入新node,
插入給定的位置,但可能存在插入位置是第一個位置
- remove(element): 從列表中移除node
- indexOf(element): 返回node在連結串列中的索引,沒有則返回-1
- removeAt(position): 移除特定位置的node,
移除特定位置,但可能移除的位置是頭部
- isEmpty(): 連結串列不包含元素,則為空
- size(): 連結串列的元素個數
- toString(): 重寫toString()方法,讓其輸出你想要的值
function LinkList() {
// 定義連結串列物件
// 定義連結串列長度
// 當前遍歷選擇的物件
let head, length, current;
// 定義Node節點類
function Node(element, next) {
this.element = element;
this.next = next;
}
this.append = function(element) {
let node = new Node(element);
// 如果連結串列中還沒有物件,新增的節點就是第一個
if (!head) {
head = node;
} else {
// 獲取連結串列最尾巴上的節點
current = head;
while (current.next) {
current = current.next;
}
// 將新增節點新增到尾巴上
current.next = node;
}
// 連結串列長度加1
length++;
};
this.insert = function(position, element) {
// position 不能小於0,並且不能大於連結串列長度
if (0 > position || position > length) {
return false;
}
let node = new Node(element);
let index = 0, previous; // 記錄current物件的索引,找到需要插入的位置
current = head;
// 如果插入位置是第一個位置,則直接將head賦值給node.next。並將node 設定為head
if (position === 0) {
node.next = head;
head = node;
} else {
while(index++ < position) {
// 記錄current所在位置的前一個物件
previous = current;
// 設定current
current = current.next;
}
// previous 應當是插入node的前一個節點,current應該是被插入node的後一個節點
previous.next = node;
node.next = current;
}
length++;
return true;
};
this.removeAt = function(position) {
// position 不能小於0,並且不能大於連結串列長度
if (0 > position || position > length) {
return null;
}
// 如果是移除頭部
if (position === 0) {
head = head.next;
} else {
// 移除中間的或者尾部
current = head;
let index = 0, previous;
// 找到需要移除的位置(current)
while (index++ < position) {
previous = current;
current = current.next;
}
// 將current的前一個node節點的next屬性指向current的下一個node節點
previous.next = current.next;
}
length--;
return current.element;
};
this.indexOf = function(element){
var current = head,
index = -1;
while (current) {
if (element === current.element) {
return index;
}
index++;
current = current.next;
}
return -1;
};
this.remove = function(element){
var index = this.indexOf(element);
return this.removeAt(index);
};
this.isEmpty = function() {
return length === 0;
};
this.size = function() {
return length;
};
this.toString = function() {
console.log(head)
}
};
複製程式碼
3. 雙向連結串列
雙向連結串列和單向連結串列的區別在於:
- 單向連結串列只能鏈向下一個節點,而雙向連結串列中next屬性鏈向下一個節點,prev屬性鏈向上一個節點
- insert(position, element): 不僅要控制next,還得控制prev指標。
插入指定位置,但可能存在插入位置是第一個位置,最後一個位置
- removeAt(postion): 同單項鍊表一樣,只是多設定一個prev指標。
移除特定位置,但可能移除的位置是頭部
function DoudlyLinkedList() {
// Node節點增加prev屬性,指向前一個node節點
function Node(element) {
this.element = element;
this.next = null;
this.prev = null;
}
// 新增tail,存放連結串列中最後一個節點
let length = 0, head = null, tail = null, current = null;
this.insert = function(element, position) {
if (position < 0 || position > length) {
return false;
}
// 需要插入的節點
let node = new Node(element);
// 如果插入節點是頭節點
if (position === 0) {
current = head;
// 第一次插入,沒有node
if (!head) {
head = node;
tail = node;
} else {
// 修改原head的prev 指向新node。 新node的next 等於原head
// 並將新node設定為head
node.next = current;
current.prev = node;
head = node;
}
// 插入位置為尾巴
} else if (position === length) {
current = tail;
current.next = node;
node.prev = current;
tail = node;
// 插入位置是任意中間位置
} else {
let previous = head;
current = head;
let i = 0;
while (i++ < position) {
previous = current;
current = current.next;
}
// previous 是需要插入位置的前一個node, current是後一個節點
node.prev = previous; // 新增的
node.next = current;
previous.next = node;
current.prev = node; // 新增
}
length++;
return true;
};
this.removeAt = function(position) {
if (position > length || position < 0) {
return null;
}
current = head;
// 如果移除的是第一個
if (position === 0 ) {
head = current.next;
// 如果只有一項,那麼tail 也得設定為null
if (length === 1) {
tail = null;
} else {
// 將head的prev索引設定為null
head.prev = null;
}
// 移除的是末尾的元素
} else if (position === length-1) {
current = tail;
tail = tail.prev;
tail.next = null;
} else {
let index = 0, previous;
while (index++ < position) {
previous = current;
current = current.next;
}
// 移除current,就是講current前後兩個節點聯絡在一起
previous.next = current.next;
current.next.prev = previous;
}
length--;
return current.element;
};
this.toString = function() {
console.log(head)
}
}
複製程式碼
4. 迴圈連結串列
迴圈連結串列 分為單項迴圈連結串列與雙向迴圈連結串列。不再編寫程式碼
- 單項迴圈連結串列: 最後一個節點
tail
的next屬性
指向 第一個節點head
- 雙向迴圈連結串列:最後一個節點
tail
的next屬性
指向第一個節點head
。第一個節點head
的prev
屬性指向最後一個節點tail
。