AngryLiverJavaScriptデータ構造-両端キュー

一緒に書く習慣をつけましょう!「ナゲッツデイリーニュープラン・4月アップデートチャレンジ」に参加して8日目です。クリックしてイベントの詳細をご覧ください

みなさん、こんにちは。私の名前はヤン・チェンゴンです。

前回の記事では、新しいデータ構造であるキューについて学びました。誰もがキューの「先入れ先出し」の原則を理解する必要があります。この記事では、両端キューと呼ばれる特別な種類のキューについて学習します。

それでもキューが何であるかわからない場合は、前の記事「AngryLivesJavaScriptデータ構造-キュー」をお読みください。

両端キューとは何ですか

キューの概念を最初に見てください。キューは、先入れ先出しFIFO )の原則に従う順序付けられたコレクションのセットです。キューのルールは、末尾に新しい要素を追加し、先頭から最も近い要素を削除することです。

キューイングの例も示しました。たとえば、朝食を買いに行って前の人に会う場合は、後ろから列に並ぶ必要があります。前の人が最初に朝食を購入し、次に次の人が向きを変えることができます。

しかし、考えてみてください。そのようなシナリオはありますか。

  1. 私は列の後ろにいて、突然緊急事態が発生したので、私はすぐに出発しなければなりませんでした
  2. お金を払って帰ったところ、突然ストローがないことに気づいたので、前に走ってストローを手に入れました。

キューの観点からは、これら2つの状況は、「先入れ先出し」の原則に違反します。これは、最後にキューに入れられた新しい要素が実際にはキューの最後からデキューされたためです。実際にデキューされました。キューの先頭からエンキューされました。これは、キュー内のスタックの「後入れ先出し」の状況に相当します。

実際、これは両端キューであり、両端キューはキューます。

次に、特定の概念を見てみましょう。両端キュー、英語名dequeは、ヘッドとテールの要素を同時に追加および削除できる特別なキューです。キューとスタックを組み合わせたデータ構造と考えてください。

コンピュータの世界でdequesの最も一般的なアプリケーションは、undoです。たとえば、テキスト編集を行っている場合、各操作は両端キューに記録されます。通常の状況では、入力したテキストはこのキューの最後から継続的に挿入されます。最大1000語などの特定の条件が満たされた場合、キューの先頭から最初のテキストを削除できます。

しかし、突然、単語が間違って書かれていることに気づきました。このとき、キーボードでctrl+zundoこのundo操作は、キューの最後から最後の単語をデキューして、単語が削除されたことを示します。

コンピューターと実際の両端キューはどちらも、「先入れ先出し」と「後入れ先出し」の原則に従います。

両端キューの実装

以前の知識を組み合わせて、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():dequeの先頭から要素を削除します(キューのdequeueメソッドと同じ)
  • removeBack():両端キューの最後から要素を削除します(スタックのpopメソッドと同じ)
  • peekFront():両端キューの最初の要素を返します(キューのpeekメソッドと同じ)
  • peekBack():両端キューの最後の要素を返します(スタックのpeekメソッドと同じ)

これらのメソッドのみ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
  }
}
复制代码

上記のコードは、両端キューの先頭にキューイングを実装します。これは3つのケースに分けられます。

ケース1:キューが空です

キューが空の場合、キューの先頭と末尾からの要素の挿入は同じです。したがって、この場合、要素を最後から挿入するメソッドを直接呼び出すことができます。

ケース2:開始の値が0より大きい

startの値が0より大きい場合は、dequeがすでにデキューされていることを意味します。この場合、新しく追加された要素を最後のデキューされた要素の位置に配置するだけで済みます。この位置のキー値は時間start - 1です。

ケース3:startの値が0に等しい

startの値が0の場合、dequeでデキューされる要素がないことを意味します。このとき、ヘッドに新しい要素を追加するには、キュー内のすべての要素のキー値を1ビット後方、つまり+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
})()
复制代码

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()); // 北京,杭州
复制代码

うーん、すべてが完璧に行われます。

要約する

この記事では、両端キューの概念を紹介し、両端キューを手動で実装します。あなたがまだそれを学んだかどうかわかりませんか?理解できない方法を見つけた場合は、列をクリックして、データ構造シリーズの以前の記事を表示してください。

この記事の出典は公式アカウントです:ProgrammerSuccessこれは、JavaScriptのデータ構造とアルゴリズムの学習の第7部です。このシリーズは1か月間更新されます。公式アカウントに注目し、[グループの追加]をクリックして学習チームに参加してください。

おすすめ

転載: juejin.im/post/7084261922576531469