DoublyLinkedList
unknown
javascript
2 years ago
9.7 kB
14
Indexable
class DoublyLinkedList {
constructor() {
this.size = 0
this.head = null
this.tail = null
}
prepend(value) {
// Создаём новый узел, который будет новым head,
// при создании передаём второй аргумент, который указывает
// что его "next" будет текущий head,
// так как новый узел будет стоять перед текущем head.
const newNode = {
value,
next: null,
prev: null,
}
// Если есть head, то он больше не будет head.
// Поэтому, его ссылку на предыдущий узел (previous) меняем на новый узел.
if (this.head) {
this.head.prev = newNode
}
// Переназначаем head на новый узел
this.head = newNode
// Если ещё нет tail, делаем новый узел tail.
if (!this.tail) {
this.tail = newNode
}
this.size++
// Возвращаем весь список.
return this
}
append(value) {
// Создаём новый узел.
const newNode = {
value,
next: null,
prev: null,
}
if (this.tail) {
// Присоединяем новый узел к концу связного списка.
this.tail.next = newNode
}
// В новом узле указываем ссылку на предыдущий (previous) элемент на this.tail,
// так как новый узел будет теперь последним.
newNode.prev = this.tail
// Переназначаем tail на новый узел.
this.tail = newNode
// Если ещё нет head, делаем новый узел head.
if (!this.head) {
this.head = newNode
}
this.size++
return this
}
// Добавляет элемент в список.
// Если указан индекс - добавляет по индексу,
// в противном случае добавляет в конец.
// Если индекс за пределами — кидает ошибку.
add(value, index = this.size) {
if (!Number.isInteger(index) || index < 0 || index > this.size + 1) {
console.log(`Invalid index. Current length is ${this.size}.`)
return this
}
// If index is 0, prepend
// If index is equal to this.length, append
if (index === 0) {
this.prepend(value)
return this
} else if (index === this.size) {
this.append(value)
return this
}
// Reach the node at that index
let newNode = {
value,
next: null,
prev: null,
}
let previousNode = this.head
for (let k = 0; k < index - 1; k++) {
previousNode = previousNode.next
}
let nextNode = previousNode.next
newNode.next = nextNode
previousNode.next = newNode
newNode.prev = previousNode
nextNode.prev = newNode
this.size++
}
// Удаляет элемент из списка по значению.
// Удаляет только первый найденный элемент.
// Если элемент не найден, ничего делать не нужно.
removeByValue(value) {
// Если нет head значит список пуст.
if (!this.head) {
return
}
let deletedNode = null
let currentNode = this.head
// Перебираем все узлы и удаляем их, если их значение равно указанному.
while (currentNode) {
if (currentNode.value === value) {
// Сохраняем значение текущего узла как удаленное.
deletedNode = currentNode
// Если head должен быть удален,
if (deletedNode === this.head) {
// то делаем следующий узел новым head
this.head = deletedNode.next
// Меняем в новом head ссылку (previous) на null.
if (this.head) {
this.head.prev = null
}
// Если все узлы в списке имеют одинаковое значение,
// которое передается в качестве аргумента,
// тогда все узлы будут удалены. Поэтому tail необходимо обновить.
if (deletedNode === this.tail) {
this.tail = null
}
} else if (deletedNode === this.tail) {
// Если tail должен быть удален, -
// меняем tail на предпоследний узел, который станет новым хвостом.
this.tail = deletedNode.prev
// Обновляем ссылку next в новом хвосте.
this.tail.next = null
} else {
// Если средний узел будет удален, -
// сохраняем ссылку на предыдущий элемент,
const previousNode = deletedNode.prev
// и сохраняем ссылку на следующий элемент.
const nextNode = deletedNode.next
// Меняем ссылки у предыдущего и следующего узлов от удаленного узла,
// чтобы они больше не ссылались на удаленный узел.
previousNode.next = nextNode
nextNode.prev = previousNode
}
this.size--
return
}
// Перематываем на один узел вперёд.
currentNode = currentNode.next
}
}
// Удаляет элемент из списка по индексу.
// Если индекс за пределами — кидает ошибку.
removeByIndex(index) {
// Если нет head значит список пуст.
if (!this.head) {
return null
}
let deletedNode = null
let i = 0
// Перебираем все узлы и удаляем их, если их значение равно указанному.
// Сохраняем значение текущего узла как удаленное.
deletedNode = this.searchByIndex(index)
// Если head должен быть удален,
if (deletedNode === this.head) {
// то делаем следующий узел новым head
this.head = deletedNode.next
// Меняем в новом head ссылку (previous) на null.
if (this.head) {
this.head.prev = null
}
// Если все узлы в списке имеют одинаковое значение,
// которое передается в качестве аргумента,
// тогда все узлы будут удалены. Поэтому tail необходимо обновить.
if (deletedNode === this.tail) {
this.tail = null
}
} else if (deletedNode === this.tail) {
// Если tail должен быть удален, -
// меняем tail на предпоследний узел, который станет новым хвостом.
this.tail = deletedNode.prev
// Обновляем ссылку next в новом хвосте.
this.tail.next = null
} else {
// Если средний узел будет удален, -
// сохраняем ссылку на предыдущий элемент,
const previousNode = deletedNode.prev
// и сохраняем ссылку на следующий элемент.
const nextNode = deletedNode.next
// Меняем ссылки у предыдущего и следующего узлов от удаленного узла,
// чтобы они больше не ссылались на удаленный узел.
previousNode.next = nextNode
nextNode.prev = previousNode
}
return deletedNode
}
// Ищет элемент в списке по индексу.
// Если индекс за пределами — кидает ошибку.
searchByIndex(index) {
// Если нет head - список пуст.
if (!this.head) {
return null
}
// Проверка неверной позиции
if (index < 0 || index > this.size) {
throw new Error('Индекс за пределами списка')
}
let currentNode = this.head
let count = 0
// Перебираем все узлы до нужного индекса.
while (count < index) {
currentNode = currentNode.next
count++
}
return currentNode
}
// Ищет элемент в списке по значению.
// Возвращает первый найденный элемент.
// Опционально можно указать индекс начала поиска.
// Если индекс за пределами — кидает ошибку.
// Если элемент не найден, нужно вернуть null.
searchByValue(value, startIndex = 0) {
// Если нет head - список пуст.
if (!this.head) {
return null
}
let currentNode = this.head
// Перебираем все узлы в поиске значения.
while (currentNode) {
// Если указано значение, пробуем сравнить его по значению.
if (value !== undefined && currentNode.value === value) {
return currentNode
}
// Перематываем на один узел вперед.
currentNode = currentNode.next
}
return null
}
}
const userObj = new DoublyLinkedList()
userObj.add('test1')
userObj.add('test2')
userObj.add('test3')
userObj.add('test4')
userObj.add('test5')
userObj.removeByValue('test111')
// console.log(userObj.searchByIndex(0))
// console.log(userObj.searchByIndex(1))
// console.log(userObj.searchByIndex(2))
console.log(userObj.size)
console.log(userObj.head)
console.log(userObj.tail)
Editor is loading...