Структура данных Angry Liver JavaScript — двусторонние очереди

Привыкайте писать вместе! Это 8-й день моего участия в «Новом ежедневном плане Nuggets · Апрельское задание по обновлению», нажмите, чтобы просмотреть подробности мероприятия .

Всем привет, меня зовут Ян Чэнгун.

В прошлой статье мы узнали о новой структуре данных — очереди. Также каждый должен понимать принцип «первым пришел, первым вышел » в очереди . В этой статье мы собираемся узнать об особом виде очереди, называемой двусторонней очередью.

Если вы до сих пор не знаете, что такое очередь, прочитайте предыдущую статью « Структуры данных Angry Lives JavaScript — очереди» .

Что такое двусторонняя очередь

Сначала рассмотрим концепцию очереди: очередь — это набор упорядоченных коллекций , которые следуют принципу «первым пришел — первым обслужен » ( FIFO ). Правило очереди состоит в том, чтобы добавлять новые элементы в хвост и удалять ближайший элемент из головы.

Мы также привели пример организации очереди. Например, если вы идете купить завтрак и видите кого-то впереди, вы должны стоять в очереди сзади, и человек впереди купит завтрак первым, а затем может повернуться следующий человек.

Но подумайте, будет ли такой сценарий:

  1. Я был в конце очереди, и вдруг возникла чрезвычайная ситуация, поэтому мне пришлось быстро уйти
  2. Я только что ушел после оплаты и вдруг понял, что у меня нет соломинки, поэтому я побежал вперед, чтобы получить ее

С точки зрения очереди, эти две ситуации нарушают принцип « первым поступил, первым вышел », потому что новый элемент, который был поставлен в очередь последним, фактически был исключен из очереди с конца очереди; первый элемент, который должен был быть удален из очереди был фактически исключен из очереди. Это эквивалентно ситуации "последний вошел, первый вышел" для стека в очереди.

По сути, это двусторонняя очередь, а двусторонняя очередь примерно равна очереди + стек .

Теперь давайте рассмотрим конкретное понятие: двухсторонняя очередь , английское название 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 sizetoString

По сравнению с обычными очередями двусторонние очереди имеют следующие методы:

  • 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. Эта серия будет обновляться в течение месяца. Добро пожаловать, чтобы обратить внимание на официальный аккаунт и нажмите « Добавить группу », чтобы присоединиться к нашей учебной команде ~

рекомендация

отjuejin.im/post/7084261922576531469
рекомендация