キューの基本
はじめに
この通り(イメージ提供:Geek Timeのコラム「データ構造とアルゴリズムの美」)。
キューの末尾のみが入り、キューの先頭のみが出ることができる記憶構造体。
シーケンシャルキュー
- 固定キューサイズ
- 送信キューと受信キューの時間複雑度はO(1)
連鎖キュー
連鎖リスト方式で実装されたキュー:
- キューサイズ無制限
- 送信キューと受信キューの時間複雑度はO(1)
一般的なアルゴリズム
サーキュラーキューの実装
循環キューは、キューの先頭と末尾をそれぞれ指すダブルポインタ head と tail によって実装されます。循環キューの実装では、そのキューが空かどうか、キューが判定で一杯かどうかに注意する必要があります。その条件は以下の通りです:
キューは空です: head == tail
キューがいっぱいです: %capacity == head
コードは以下の通り:
class LoopQueue{
int[] queue = null;
int capacity = 0;
int head = 0;
int tail = 0;
public LoopQueue(int capacity) {
this.capacity = capacity;
queue = new int[capacity];
}
public void add(int val){
if((tail + 1) % capacity == head){
System.out.println("キューが一杯になった");
}else {
System.out.println("値を挿入する+val);
tail = tail%capacity;
queue[tail] = val;
tail++;
}
}
public void remove(){
if(head%capacity == tail){
System.out.println("キューは空である");
}else {
head = head%capacity;
int val = queue[head];
System.out.println("の値を削除する。+val);
head++;
}
}
}
円形ダブルエンド待ち行列の設計
ダブルエンド待ち行列の実装を設計する。
実装は以下の操作をサポートする必要がある:
MyCircularDeque(k)コンストラクタ,サイズkのダブルエンド待ち行列。
insertFront()両端キューの先頭に要素を追加する。 操作が成功すれば真を返す。
insertLast()ダブルエンド・キューの末尾に要素を追加する。操作が成功すれば真を返す。
deleteFront()ダブルエンド待ち行列の先頭から要素を削除する。 成功すれば真を返す。
deleteLast()両端キューの末尾から要素を取り出す。操作が成功すれば真を返す。
getFront()両端キューの先頭から要素を取得する。両端待ち行列が空の場合は-1を返す。
getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。
isEmpty()ダブルエンド・キューが空かどうかをチェックする。
isFull()ダブルエンド待ち行列が満杯かどうかをチェックする
MyCircularDeque circularDeque = new MycircularDeque(3); // 容量サイズを3に設定する
circularDeque.insertLast(1); // 真を返す
circularDeque.insertLast(2); // 真を返す
circularDeque.insertFront(3); // 真を返す
circularDeque.insertFront(4); // が満杯の場合、falseを返す
circularDeque.getRear(); // 2を返す
circularDeque.isFull(); // 真を返す
circularDeque.deleteLast(); // 真を返す
circularDeque.insertFront(4); // 真を返す
circularDeque.getFront(); // 戻り値4
コードは以下の通り:
class MyCircularDeque {
class Node{
Node next;
int value;
Node(int value){
this.value = value;
}
}
Node head;
Node tail;
int count = 0;
int capcity;
/** Initialize your data structure here. Set the size of the deque to be k. */
public MyCircularDeque(int k) {
head = new Node(-1);
tail = new Node(-1);
capcity = k;
}
/** Adds an item at the front of Deque. Return true if the operation is successful. */
public boolean insertFront(int value) {
if(count >= capcity)return false;
Node next = new Node(value);
if(count == 0){
head.next = next;
tail.next = next;
}else{
Node p = head.next;
head.next = next;
next.next = p;
}
count++;
return true;
}
/** Adds an item at the rear of Deque. Return true if the operation is successful. */
public boolean insertLast(int value) {
if(count >= capcity)return false;
Node next = new Node(value);
if(count == 0){
head.next = next;
tail.next = next;
}else{
Node p = tail.next;
tail.next = next;
p.next = next;
}
count++;
return true;
}
/** Deletes an item from the front of Deque. Return true if the operation is successful. */
public boolean deleteFront() {
if(count == 0)return false;
if(count == 1){
head.next = null;
tail.next = null;
}else{
Node p = head.next;
head.next = p.next;
p.next = null;
}
count--;
return true;
}
/** Deletes an item from the rear of Deque. Return true if the operation is successful. */
public boolean deleteLast() {
if(count == 0)return false;
if(count == 1){
head.next = null;
tail.next = null;
}else{
Node p = tail.next;
Node root = head.next;
while(root!=null&&root.next != p)root = root.next;
tail.next = root;
root.next = null;
}
count--;
return true;
}
/** Get the front item from the deque. */
public int getFront() {
if(count == 0)return -1;
Node node = head.next;
return node.value;
}
/** Get the last item from the deque. */
public int getRear() {
if(count == 0)return -1;
Node node = tail.next;
return node.value;
}
/** Checks whether the circular deque is empty or not. */
public boolean isEmpty() {
return count == 0;
}
/** Checks whether the circular deque is full or not. */
public boolean isFull() {
return count == capcity;
}
}
スライディングウィンドウの最大値
配列 nums とサイズ k のスライディング・ウィンドウが与えられたとき,すべてのスライディング・ウィンドウにおける最大値を求めよ.
:
: nums = [1,3,-1,-3,5,3,6,7], とk = 3
: [3,3,5,5,6,7]
:
スライディングウィンドウの最大位置
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
アイデアの実現:
ウィンドウをキューと考え、各スライドが発信と着信の操作になります。
方法1:キュー内の最大値を毎回比較して求める場合、キューのサイズがkであるため、時間の複雑さはO(n * k)となります。
方法2:キューヘッド要素のキューからスライディングウィンドウの値は、キューのヘッド要素がキューから出るように、キューにスライディングウィンドウの値は、キューの終わりよりも、キューからキューの終わりを聞かせて、キュー要素の終わりまで周回し続けるキュー要素またはキューが空であるよりも大きいですし、要素を挿入するキューの最後に。これは、キューがソートの逆順の値を満たすことができるようになり、毎回、最大値である先頭の要素を取るだけです。
コードは以下の通り:
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length == 0)return new int[0];
if(k == 1)return nums;
int[] res = new int[nums.length - k + 1];
Deque<Integer> l = new LinkedList<>();
for(int i = 0;i < k;i++){
if(l.isEmpty())l.add(nums[i]);
else{
while(!l.isEmpty() && nums[i] > l.peekLast()){
l.pollLast();
}
l.add(nums[i]);
}
}
res[0] = l.peekFirst();
for(int i = k;i < nums.length;i++){
if(nums[i - k] == l.peekFirst())l.pollFirst();
while(!l.isEmpty() && nums[i] > l.peekLast()){
l.pollLast();
}
l.add(nums[i]);
res[i - k + 1] = l.peekFirst();
}
return res;
}
}
必須コードの実装
- 配列によるシーケンシャル・キューの実装
- リンクされたテーブルによる連鎖キューの実装
- サーキュラーキューの実装
- 円形ダブルエンド待ち行列の設計
- スライディングウィンドウの最大値





