链表-循环链表
循环链表可以像链表一样只有单向引用,也可以像双向链表一样有双向引用。循环链表和链表之间唯一的区别在于
最后一个元素指向下一个元素的指针(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
至次, 循环链表也基本理解差不多了, 还是比较容易实现的, 通过形象化的想象出来.
耐心和恒心, 总会获得回报的.