迴圈連結串列可以像連結串列一樣只有單向引用,也可以像雙向連結串列一樣有雙向引用。迴圈連結串列和連結串列之間唯一的區別在於
最後一個元素指向下一個元素的指標(tail.next)不是引用undefined,而是指向第一個元素(head).
- 單連結串列: this.tail.next = this.head;
- 雙向連結串列: this.tail.next = this.head; this.head.prev = this.tail;
這裡簡化演示就單向連結串列的方式來操作即可;
連結串列初始化
// 節點類
class Node {
constructor(element) {
this.element = element;
this.next = undefined;
}
}
// 連結串列類
class CicularLinkedList {
constructor() {
this.count = 0;
this.head = null;
}
size() {
return this.count
}
toString() {
if (this.head == null) return undefined
let str = `${this.head.element}`
let current = this.head.next
for (let i = 1; i < this.count; i++) {
str = `${str}, ${current.element}`
current = current.next
}
}
getElementAt(index) {
if (index < 0 || index > this.count) return undefined
let node = this.head;
for (let i = 0; i < index; i++) {
node = node.next;
}
return node;
}
indexOf(element) {
let current = this.head;
for (let i = 0; i < this.count; i++) {
if (current.element == element) return i;
current = current.next;
}
return -1;
}
}
在任意位置插入元素
- 頭結點插入
- 空連結串列, 自迴圈
- 非空連結串列, 記得透過 getElement() 方法將 tail 元素指向 head
- 中間或尾部插入, 調整 previous, current 即可
// 在任意位置插入元素
insert(index, element) {
if (index < 0 || index > this.count) return false
const node = new Node(element)
let current = this.head
if (index == 0) {
// 空連結串列插入元素, 則自迴圈唄
if (this.head == null) {
this.head = node
node.next = this.head
} else {
// 記得要將最後一個元素的 next 指向頭結點元素
node.next = current
this.head = node
const tail = this.getElementAt(this.size())
tail.next = this.head
}
} else {
// 從中間或者尾部插入, 調整 previous, current 指標即可
const previous = this.getElementAt(index - 1)
current = previous.next
previous.next = node
node.next = current
}
// 記得更新連結串列長度
this.count++
return true
}
和普通連結串列一樣的, 唯一要考慮的就是要將尾節點的 next 連線到頭節點
在任意位置刪除元素
- 刪除頭節點
- 空連結串列, 返回 undefined
- 連結串列僅一個元素, 讓 head ->null 即可
- 連結串列有多個元素, 讓 head -> 原第二; tail -> 原第二 (現第一)
- 中間或尾部刪, 調整 previous , current 即可
// 從任意位置刪除元素
removeAt(index) {
// 越界和空連結串列檢查
if (index < 0 || index > this.count) return undefined
if (this.head == null) return undefined
let current = this.head
if (index == 0) {
if (this.count == 1) {
// 刪除頭節點, 且當只有一個元素時, 讓 head -> null 即可
this.head = undefined
} else {
// 刪除頭結點, 後面原來還有多個元素, head -> 原第二, tail -> 原第二(現第一 )
current = this.head
this.head = this.head.next
const tail = this.getElementAt(this.count)
tail.next = this.head.next
}
} else {
// 刪除中間或者尾部元素, 不用常更新 tail
const previous = this.getElementAt(index - 1)
current = previous.next
previous.next = current.next
}
// 記得更新連結串列長度
this.count--
return current.element
}
// 刪除元素
remove(element) {
const index = this.indexOf(element)
return this.removeAt(index)
}
}
迴圈連結串列-完整實現
// 節點類
class Node {
constructor(element) {
this.element = element
this.next = undefined
}
}
// 連結串列類
class CicularLinkedList {
constructor() {
this.count = 0
this.head = null
}
size() {
return this.count
}
toString() {
if (this.head == null) return undefined
let str = `${this.head.element}`
let current = this.head.next
for (let i = 1; i < this.count; i++) {
str = `${str}, ${current.element}`
current = current.next
}
return str
}
getElementAt(index) {
if (index < 0 || index > this.count) return undefined
let node = this.head;
for (let i = 0; i < index; i++) {
node = node.next
}
return node
}
indexOf(element) {
let current = this.head;
for (let i = 0; i < this.count; i++) {
if (current.element == element) return i
current = current.next
}
return -1
}
// 在任意位置插入元素
insert(index, element) {
if (index < 0 || index > this.count) return false
const node = new Node(element)
let current = this.head
if (index == 0) {
// 空連結串列插入元素, 則自迴圈唄
if (this.head == null) {
this.head = node
node.next = this.head
} else {
// 記得要將最後一個元素的 next 指向頭結點元素
node.next = current
this.head = node
const tail = this.getElementAt(this.size())
tail.next = this.head
}
} else {
// 從中間或者尾部插入, 調整 previous, current 指標即可
const previous = this.getElementAt(index - 1)
current = previous.next
previous.next = node
node.next = current
}
// 記得更新連結串列長度
this.count++
return true
}
// 從任意位置刪除元素
removeAt(index) {
// 越界和空連結串列檢查
if (index < 0 || index > this.count) return undefined
if (this.head == null) return undefined
let current = this.head
if (index == 0) {
if (this.count == 1) {
// 刪除頭節點, 且當只有一個元素時, 讓 head -> null 即可
this.head = undefined
} else {
// 刪除頭結點, 後面原來還有多個元素, head -> 原第二, tail -> 原第二(現第一 )
current = this.head
this.head = this.head.next
const tail = this.getElementAt(this.count)
tail.next = this.head.next
}
} else {
// 刪除中間或者尾部元素, 不用常更新 tail
const previous = this.getElementAt(index - 1)
current = previous.next
previous.next = current.next
}
// 記得更新連結串列長度
this.count--
return current.element
}
// 刪除元素
remove(element) {
const index = this.indexOf(element)
return this.removeAt(index)
}
}
// test
const list = new CicularLinkedList()
list.insert(0, '111')
console.log('連結串列是: ', list.toString());
list.insert(1, '222')
console.log('連結串列是: ', list.toString());
list.insert(2, '333')
console.log('連結串列是: ', list.toString());
list.insert(list.size(), '444')
console.log('連結串列是: ', list.toString());
list.insert(0, '555')
console.log('連結串列是: ', list.toString());
console.log('連結串列長度是: ', list.size());
console.log('尾元素的 next 是: ', list.getElementAt(list.size() - 1).next.element);
console.log('刪除頭部元素: ', list.removeAt(0));
console.log('連結串列是: ', list.toString());
console.log('連結串列長度是: ', list.size());
console.log('刪除位置 1 的元素: ', list.removeAt(1));
console.log('連結串列是: ', list.toString());
console.log('連結串列長度是: ', list.size());
console.log('刪除尾部部元素: ', list.removeAt(list.size() - 1 ));
console.log('連結串列是: ', list.toString());
console.log('連結串列長度是: ', list.size());
測試結果如下:
PS E:\Front_Mook\projects\shop-admin> node test.js
連結串列是: 111
連結串列是: 111, 222
連結串列是: 111, 222, 333
連結串列是: 111, 222, 333, 444
連結串列是: 555, 111, 222, 333, 444
連結串列長度是: 5
尾元素的 next 是: 555
刪除頭部元素: 555
連結串列是: 111, 222, 333, 444
連結串列長度是: 4
刪除位置 1 的元素: 222
連結串列是: 111, 333, 444
連結串列長度是: 3
刪除尾部部元素: 444
連結串列是: 111, 333
連結串列長度是: 2
至次, 迴圈連結串列也基本理解差不多了, 還是比較容易實現的, 透過形象化的想象出來.