Привыкайте писать вместе! Это 8-й день моего участия в «Новом ежедневном плане Nuggets · Апрельское задание по обновлению», нажмите, чтобы просмотреть подробности мероприятия .
Всем привет, меня зовут Ян Чэнгун.
В прошлой статье мы узнали о новой структуре данных — очереди. Также каждый должен понимать принцип «первым пришел, первым вышел » в очереди . В этой статье мы собираемся узнать об особом виде очереди, называемой двусторонней очередью.
Если вы до сих пор не знаете, что такое очередь, прочитайте предыдущую статью « Структуры данных Angry Lives JavaScript — очереди» .
Что такое двусторонняя очередь
Сначала рассмотрим концепцию очереди: очередь — это набор упорядоченных коллекций , которые следуют принципу «первым пришел — первым обслужен » ( FIFO ). Правило очереди состоит в том, чтобы добавлять новые элементы в хвост и удалять ближайший элемент из головы.
Мы также привели пример организации очереди. Например, если вы идете купить завтрак и видите кого-то впереди, вы должны стоять в очереди сзади, и человек впереди купит завтрак первым, а затем может повернуться следующий человек.
Но подумайте, будет ли такой сценарий:
- Я был в конце очереди, и вдруг возникла чрезвычайная ситуация, поэтому мне пришлось быстро уйти
- Я только что ушел после оплаты и вдруг понял, что у меня нет соломинки, поэтому я побежал вперед, чтобы получить ее
С точки зрения очереди, эти две ситуации нарушают принцип « первым поступил, первым вышел », потому что новый элемент, который был поставлен в очередь последним, фактически был исключен из очереди с конца очереди; первый элемент, который должен был быть удален из очереди был фактически исключен из очереди. Это эквивалентно ситуации "последний вошел, первый вышел" для стека в очереди.
По сути, это двусторонняя очередь, а двусторонняя очередь примерно равна очереди + стек .
Теперь давайте рассмотрим конкретное понятие: двухсторонняя очередь , английское название deque , — это особая очередь, которая позволяет нам добавлять и удалять элементы из головы и хвоста одновременно. Думайте об этом как о структуре данных, которая сочетает в себе очередь и стек.
Наиболее распространенное применение деков в компьютерном мире — отмена . Например, если вы занимаетесь редактированием текста, каждая операция записывается в очередь. При нормальных обстоятельствах текст, который вы вводите, будет непрерывно вставляться с конца этой очереди. При выполнении определенных условий, таких как не более 1000 слов, вы можете удалить первый текст из начала очереди.
Но вы вдруг обнаружите, что слово написано неправильно. В это время вы можете использовать ctrl+z
отмену Эта операция отмены состоит в том, чтобы исключить из очереди последнее слово из конца очереди, указывая на то, что слово было удалено.
И компьютеры, и реальные деки следуют принципам «первым пришел, первым вышел» и «последним пришел — первым ушел».
Реализация двусторонней очереди
Объединив предыдущие знания, мы реализуем двустороннюю очередь на основе объектов JavaScript.
const Deque = (()=> {
let _start;
let _end;
let _items;
class Deque {
constructor() {
_start = 0;
_end = 0;
_items = 0;
}
}
return Deque
})()
复制代码
Как и в приведенном выше коде, items
атрибут используется для хранения элементов двусторонней очереди, которая точно такая же, как и предыдущая очередь.
Поскольку двусторонняя очередь — это тоже очередь, основные методы очереди, такие как и isEmpty
, точно такие же, как описанные в предыдущей очереди, поэтому я не буду вдаваться в подробности. Кому нужно знать, можете перейти по ссылке в начале статьи.clear
size
toString
По сравнению с обычными очередями двусторонние очереди имеют следующие методы:
addFront()
: добавить элемент из головы двухсторонней очередиaddBack()
: добавить элементы из конца двухсторонней очереди (так же, как метод enqueue очереди)removeFront()
: удаляет элемент из головы двухсторонней очереди (так же, как метод dequeue очереди)removeBack()
: удаляет элемент из конца двухсторонней очереди (так же, как метод pop в стеке)peekFront()
: возвращает первый элемент двухсторонней очереди (так же, как метод просмотра очереди)peekBack()
: возвращает последний элемент двухсторонней очереди (аналогично методу просмотра стека).
Только addFront()
эти уникальны для деков, другие методы можно найти в реализациях стека и очереди. Итак, давайте посмотрим, как реализован этот уникальный метод:
addFront(item) {
if(this.isEmpty()) {
return this.addBack(item)
}
if(_start>0) {
_start--;
_items[_start] = item
} else {
for(let i = 0; i<_end; i++) {
_items[i+1] = _items[i]
}
_end++;
_start = 0;
_items[0] = item
}
}
复制代码
Приведенный выше код реализует организацию очереди в начале двусторонней очереди, которая разделена на три случая.
Случай 1: очередь пуста
Если очередь пуста, вставка элементов из головы и хвоста очереди одинакова. Так что в этом случае мы можем напрямую вызывать метод вставки элементов с конца.
Случай 2: значение start больше 0
Значение start больше 0 означает, что двухсторонняя очередь уже удалена из очереди.В этом случае нам нужно только поместить вновь добавленный элемент в позицию последнего удаленного из очереди элемента. Ключевой ценностью этой позиции является время start - 1
.
Случай 3: значение start равно 0
Значение start, равное 0, означает, что ни один элемент не удален из очереди в очереди. В это время, чтобы добавить новый элемент в голову, переместите значение ключа всех элементов в очереди на один бит назад, то есть +1. Пусть ключевое значение нового элемента равно 0.
Другие методы были представлены в статье о стеке и очереди. Полный код выглядит следующим образом:
const Deque = (() => {
let _start
let _end
let _items
class Deque {
constructor() {
_start = 0
_end = 0
_items = 0
}
// 头部入列
addFront(item) {
if (this.isEmpty()) {
return this.addBack(item)
}
if (_start > 0) {
_start--
_items[_start] = item
} else {
for (let i = 0; i < _end; i++) {
_items[i + 1] = _items[i]
}
_end++
_start = 0
_items[0] = item
}
}
// 尾部入列
addBack(item) {
_items[_end] = item
_end++;
}
// 头部出列
removeFront() {
if (this.isEmpty()) {
return undefind
}
let item = _items[_start]
delete _items[_start]
_start++;
return item
}
// 尾部出列
removeBack() {
if (this.isEmpty()) {
return undefined
}
_end--;
let item = _items[_end]
delete _items[_end]
return item
}
// 头部第一个元素
peekFront() {
if (this.isEmpty()) {
return undefined
}
return _items[_start]
}
// 尾部第一个元素
peekBack() {
if (this.isEmpty()) {
return undefined
}
return _items[_end]
}
size() {
return _end - _start
}
isEmpty() {
return _end - _start === 0
}
clear() {
_items = {}
_start = 0
_end = 0
}
toString() {
let arr = Object.values(_items)
return arr.toString()
}
}
return Deque
})()
复制代码
использовать деку
Предыдущий полный код относительно длинный, давайте сначала попробуем его:
// 实例化
var deque = new Deque();
console.log(deque.isEmpty()); // true
复制代码
Сначала попробуйте результат постановки головы в очередь:
deque.addFront("北京");
deque.addFront("上海");
console.log(deque.size()); // 2
console.log(deque.toString()); // 上海,北京
复制代码
С просмотром результата печати проблем нет, попробуем еще раз ввести столбец в конце:
deque.addBack("杭州");
deque.addBack("南京");
console.log(deque.size()); // 4
console.log(deque.toString()); // 上海,北京,杭州,南京
复制代码
Хвостовая запись также ожидаема, и, наконец, посмотрите на удаление из очереди:
// 头部出列
console.log(deque.removeFront()); // 上海
// 尾部出列
console.log(deque.removeBack()); // 南京
console.log(deque.size()); // 2
console.log(deque.toString()); // 北京,杭州
复制代码
Мммм, все сделано отлично.
Подведем итог
В этой статье представлена концепция двусторонних очередей и вручную реализована двусторонняя очередь. Я не знаю, выучили ли вы его еще? Если вы обнаружите, что какие-то методы вам непонятны, щелкните столбец, чтобы просмотреть предыдущие статьи из серии статей о структурах данных.
Источником статьи является официальный аккаунт: Programmer Success . Это седьмая часть изучения структур данных и алгоритмов JavaScript. Эта серия будет обновляться в течение месяца. Добро пожаловать, чтобы обратить внимание на официальный аккаунт и нажмите « Добавить группу », чтобы присоединиться к нашей учебной команде ~