链表结构的定义
链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的。每个元素由一个存储元素本身的节点和指向下一个元素的引用(也称为指针或链接)组成。下面是单向链表元素节点的定义:
class Node {
constructor (element) {
this.element = element
this.next = null
}
}
以下是双向链表元素节点定义:
class DoublyNode {
constructor (element) {
this.element = element
this.next = null
this.prev = null
}
}
单链表
1.首先,我们来创建一个单链表类:
class LinkedList {
constructor (equalsFn = defaultEquals) { // 这里需要设置一个比较函数,用于查找元素时的判定
this.count = 0 // 链表元素总数
this.head = null // 头结点
}
}
2.单链表的基本操作如下:
- push(element)-向链表尾部添加一个新元素(也可以成为add之类的名称)
- insert(element, position)-向链表的特定位置插入一个新元素。
- getElementAt(index)-返回链表中特定位置的元素。如果查找不到,返回undefined。
- remove(element)-从链表中移除一个元素。
- indexOf(element)-返回元素在链表中的索引。如果链表中没有该元素返回-1。
- removeAt(position)-从链表的特定位置移除一个元素。
- isEmpty()-如果链表中不包含任何元素,返回true,否则返回false。
- size()-返回链表中包含元素的总数。
- toString()-返回表示整个链表的字符串。
下面我们就一个个实现:
class LinkedList {
constructor (equalsFn = defaultEquals) {
this.count = 0
this.head = null
}
push (element) { // 向链表尾部添加元素
const node = new Node(element)
let current = this.head
if (current == null) { // 空链表
this.head = node // 这里不要写成current = node,因为current是this.head的副本
} else {
while (current.next != null) {
current = current.next
}
current.next = node
}
this.count++
}
removeAt(index) { // 从链表中移除指定下标的元素
if (index >= 0 && index <= this.count) { // 越界检测
let current = this.head
if (index == 0) {
this.head = current.next
} else {
let previous = null
for (let i = 0;i < index; i++) { // 迭代找到index的前一个节点和当前节点
previous = current
current = current.next
}
previous.next = current.next // 当index的前一个节点指向当前节点的后面节点,可能为null
}
this.count--
return current.element
}
return undefined
}
getElementAt (index) {
if (index >= 0 && index <= this.count) {
let node = this.head
for (let i = 0; i < index && node != null; i++) { // 迭代找到元素
node = node.next
}
return node
}
return undefined
}
insert (element, index) {
if (index >= 0 && index <= this.count) {
const node = new Node(element)
if (index == 0) {
const current = this.head
node.next = current
this.head = node
} else {
const previous = this.getElementAt(index - 1)
const current = previous.next
node.next = current
previous.next = node
}
this.count++
return true
}
return false
}
indexOf (element) {
let current = this.head
let i = 0
while(current != null) {
if (this.equalsFn(element, current.element) { // 这里的比较函数是可以自定义
return i
}
current = current.next
i++
}
return -1
}
remove (element) {
cosnt index = this.indexOf(element)
return this.removeAt(index)
}
isEmpty () {
return this.count == 0
}
size () {
return this.count
}
toString() {
if (this.head == null) {
return ''
}
/* 这一段也可以改造-下面一段代码就是改造的
let objString = `${this.head.element}`
let current = this.head.next
while (current != null) {
objString = `${objString}, ${current.element}`
current = current.next
}
*/
let objString = ''
let current = this.head
while (current != null ) {
objString += `${current.element}`
current = current.next
if (current != null) {
objString += ','
}
}
return objString
}
}
双向链表
下面简单说下双向链表中的一些方法:
// 1.插入:
insert(element, index) {
if (index >= 0 && index <= this.count) {
const node = new DoublyNode (element)
let current = this.head
if (index == 0) { // 如果是首元素
if (this.head == null) { // 空表,直接设置head、tail
this.head = node
this.tail = node
} else { // 非空表
node.next = this.head
current.prev = node
this.head = node
}
} else if (index == this.count) { // 如果是尾元素
current = this.tail
current.next = node
node.prev = current
this.tail = node
} else { // 其他位置的元素
const previous = this.getElement(index - 1)
current = previous.next
node.next = current
previous.next = node
current.prev = node
node.prev = previous
}
this.count++
return true
}
return false
}
// 2.删除
removeAt(index) {
if (index >= 0 && index <= this.count) { // 越界检测
let current = this.head // 设置为首元素
if (index == 0) { // 首元素删除
this.head = current.next
if (this.count == 1) { // 如果是单元素,就设置尾指针为null
this.tail = null
} else { // 非单元素链表,设置删除后的元素的前节点(已经设置为首元素)为null
this.head.prev = null
}
} else if (index == this.count - 1) { // 如果是尾元素
current = this.tail // 获取尾元素
this.tail = current.prev //将尾结点设置为尾元素的前一个节点
this.tail.next = null // 尾元素置空
} else { // 其他位置元素
current = this.getElementAt(index)
const previous = current.prev
previous.next = current.next
current.next.prev = previous
}
this.count--
return current.element
}
return undefined
}
循环链表
这里我们介绍下循环单链表的两个方法:
// 1.插入元素
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
current = this.getElementAt(this.size() - 1) // 获取尾元素
this.head = node
current.next = this.head // 更新尾元素的指针,形成闭环
}
} 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 = null
} else {
const removed = this.head
current = this.getElementAt(this.size())
this.head = this.head.next
current.next = this.head
current = removed
}
} else {
const previous = this.getElementAt(index - 1)
current = previous.next
previous.next = current.next
}
this.count--
return current.element
}
return undefined
}