キューの基本
序章
写真に示すように (写真は Geek Time の「データ構造とアルゴリズムの美しさ」コラムから引用)
キューの末尾のみがキューに入ることができ、キューの先頭は排出されます (つまり、先入れ先出しの記憶構造)。
シーケンシャルキュー
配列を使用して実装されたキュー、一般的なインタビューでよくテストされるキューは循環キューです (実装については後述します)。このキューの特徴は次のとおりです。
- 固定キューサイズ
- デキューとエンキューの時間計算量は O(1) です
連鎖されたキュー
リンク リストの形式で実装されたキュー。キューの特徴は次のとおりです。
- 無制限のキューサイズ
- デキューとエンキューの時間計算量は O(1) です
共通アルゴリズム
循環キューを実装する
循環キューは、キューの先頭と末尾をそれぞれ指すダブル ポインタ head と tail によって実現されます。循環キューを実装する場合、キューが空か満杯かどうかの判断に注意する必要があります。条件は次のとおりです。
キューが空です: head == tail
キューがいっぱいです: (tail + 1)%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():将一个元素添加到双端队列头部。 如果操作成功返回 true。
insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。
deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true。
deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。
getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。
getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。
isEmpty():检查双端队列是否为空。
isFull():检查双端队列是否满
示例:
MyCircularDeque circularDeque = new MycircularDeque(3); // 设置容量大小为3
circularDeque.insertLast(1); // 返回 true
circularDeque.insertLast(2); // 返回 true
circularDeque.insertFront(3); // 返回 true
circularDeque.insertFront(4); // 已经满了,返回 false
circularDeque.getRear(); // 返回 2
circularDeque.isFull(); // 返回 true
circularDeque.deleteLast(); // 返回 true
circularDeque.insertFront(4); // 返回 true
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;
}
}
習得する必要があるコードの実装
- 配列を使用してシーケンシャルキューを実装する
- リンクされたリストを使用して連鎖キューを実装する
- 循環キューを実装する