一、循环链表
循环链表和链表之间唯一的区别在于,最后一个元素指向下一个元素的指针(tail.next)不是引用undefined,而是指向第一个元素(head),如下图:
双向循环链表有指向 head 元素的 tail.next 和指向 tail 元素的 head.prev。如下图:
不需要任何额外的属性,所以直接扩展链表类并覆盖需要改写的方法即可。
1.在任意位置插入新元素
向循环链表中插入元素的逻辑和向普通链表中插入元素的逻辑是一样的。不同之处在于我们需要将循环链表尾部节点的 next 引用指向头部节点。 在头部插入元素,且链表为空情况过程如下:
非空循环链表的第一个位置插入元素过程:
insert(element, index) {
if (index >= 0 && index <= this.count) {
const node = new Node(element)
let current = this.head
if (index === 0) {
if (this.head == null) {
this.head = node // 将新节点赋值给头节点
node.next = this.head // 将新节点的next指向头节点
} else {
node.next = current // 将新节点的next指向头节点
current = this.getElementAt(this.size()) // 获取尾节点
this.head = node // 将头节点指向新节点
current.next = this.head // 将尾节点的next指向头节点
}
} else {
// 在循环链表中间插入新元素,对循环链表的第一个和最后一个节点没有做任何修改
const previous = this.getElementAt(index - 1)
node.next = previous.next
previous.next = node
}
this.count++
return true
}
return false
}
2.从任意位置移除元素
非空循环链表中移除第一个元素过程:
removeAt(index) {
if (index >= 0 && index < this.count) {
let current = this.head
if (index === 0) {
if (this.size() === 1) {
this.head = undefined
} else {
const removed = this.head
current = this.getElementAt(this.size()) // {2}获取尾节点
this.head = this.head.next // {3}将head指向第二个元素
current.next = this.head // {4}将尾节点的next指向head
current = removed // {5}将current指向被删除的节点
}
} else {
const previous = this.getElementAt(index - 1)
current = previous.next
previous.next = current.next
}
this.count--
return current.element
}
return undefined
}
二、有序链表
有序链表是指保持元素有序的链表结构。
1.声明有序链表
// 定义常量
const Compare = {
LESS_THAN: -1,
BIGGER_THAN: 1
}
// 比较元素
function defaultCompare(a, b) {
if (a === b) {
return 0;
}
return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN;
}
class SortedLinkedList extends LinkedList {
constructor(compareFn = defaultCompare) {
super()
this.compareFn = compareFn
}
}
2.找到插入元素的位置
getIndexNextSortedElement(element) {
let current = this.head
let i = 0
for (; i < this.size() && current; i++) {
const comp = this.compareFn(element, current.element)
if (comp === Compare.LESS_THAN) {
return i
}
current = current.next
}
return i
}
3.向链表尾部添加元素
push(element) {
if (this.isEmpty()) {
super.push(element);
} else {
const index = this.getIndexNextSortedElement(element);
super.insert(element, index);
}
}
4.有序插入元素
insert(element, index = 0) {
if (this.isEmpty()) {
return super.insert(element, 0)
}
const pos = this.getIndexNextSortedElement(element)
return super.insert(element, pos)
}
三、使用双向链表创建栈
当需要添加和移除很多元素时,最好选择链表而非数组
import DoublyLinkedList from './doubly-linked-list';
export default class StackLinkedList {
constructor() {
this.items = new DoublyLinkedList();
}
push(element) {
this.items.push(element);
}
pop() {
if (this.isEmpty()) {
return undefined;
}
const result = this.items.removeAt(this.size() - 1);
return result;
}
peek() {
if (this.isEmpty()) {
return undefined;
}
return this.items.getElementAt(this.size() - 1).element;
}
isEmpty() {
return this.items.isEmpty();
}
size() {
return this.items.size();
}
clear() {
this.items.clear();
}
toString() {
return this.items.toString();
}
}