循环队列 循环队列浪费一个储存空间,是因为要区别空队列和满队列,
public class CircularQueue {
// 数组:items,数组大小:n
private String[] items;
private int n = 0;
// head 表示队头下标,tail 表示队尾下标
private int head = 0;
private int tail = 0;
// 申请一个大小为 capacity 的数组
public CircularQueue(int capacity) {
items = new String[capacity];
n = capacity;
}
// 入队
public boolean enqueue(String item) {
// 队列满了
if ((tail + 1) % n == head) return false;
items[tail] = item;
tail = (tail + 1) % n;
return true;
}
// 出队
public String dequeue() {
// 如果 head == tail 表示队列为空
if (head == tail) return null;
String ret = items[head];
head = (head + 1) % n;
return ret;
}
}
LeetCode
1 设计循环双端队列
不知道效率为啥那么低,有时间再优化
class MyCircularDeque {
private:
int* items;
int n;
int head=0;
int tail=0;
public:
/** Initialize your data structure here. Set the size of the deque to be k. */
MyCircularDeque(int k) {
items=new int[k+1];
n=k+1;
}
/** Adds an item at the front of Deque. Return true if the operation is successful. */
bool insertFront(int value) {
if ((tail + 1) % n == head) return false;
head = (head+n-1) % n;
items[head] = value;
return true;
}
/** Adds an item at the rear of Deque. Return true if the operation is successful. */
bool insertLast(int value) {
if ((tail + 1) % n == head) return false;
items[tail] = value;
tail = (tail + 1) % n;
return true;
}
/** Deletes an item from the front of Deque. Return true if the operation is successful. */
bool deleteFront() {
if (head == tail) return false;
//int ret = items[head];
head = (head + 1) % n;
return true;
}
/** Deletes an item from the rear of Deque. Return true if the operation is successful. */
bool deleteLast() {
// 如果 head == tail 表示队列为空
if (head == tail) return false;
//int ret = items[head];
tail = (tail+n-1) % n;
return true;
}
/** Get the front item from the deque. */
int getFront() {
if(isEmpty()){
return -1;
}else {
return items[head];
}
}
/** Get the last item from the deque. */
int getRear() {
if(isEmpty()){
return -1;
}else {
return items[(tail+n-1) % n];
}
}
/** Checks whether the circular deque is empty or not. */
bool isEmpty() {
if (head == tail) return true;
else return false;
}
/** Checks whether the circular deque is full or not. */
bool isFull() {
if ((tail + 1) % n == head) return true;
else return false;
}
};
/**
* Your MyCircularDeque object will be instantiated and called as such:
* MyCircularDeque* obj = new MyCircularDeque(k);
* bool param_1 = obj->insertFront(value);
* bool param_2 = obj->insertLast(value);
* bool param_3 = obj->deleteFront();
* bool param_4 = obj->deleteLast();
* int param_5 = obj->getFront();
* int param_6 = obj->getRear();
* bool param_7 = obj->isEmpty();
* bool param_8 = obj->isFull();
*/
2 滑动窗口最大值
使用双端队列
双端队列(deque,全名double-ended queue),是一种具有队列和栈的性质的数据结构。
双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。双端队列可以在队列任意一端入队和出队。
我声明了一个变量 deque<int>window,用于存储下标。这个变量有以下 特点:
变量的最前端(也就是 window.front())是此次遍历的最大值的下标
当我们遇到新的数时,将新的数和双项队列的末尾(也就是window.back())比较,如果末尾比新数小,则把末尾扔掉,直到该队列的末尾比新数大或者队列为空的时候才停止,做法有点像使用栈进行括号匹配。
双项队列中的所有值都要在窗口范围内
特点一只是方便我们获取每次窗口滑动一格之后的最大值,我们可以直接通过 window.front() 获得
通过特点二,可以保证队列里的元素是从头到尾降序的,由于队列里只有窗口内的数,所以他们其实就是窗口内第一大,第二大,第三大... 的数。
特点三就是根据题意设置的。但我们实际上只用比较现在的下标和 window.front() 就可以了,想想为什么?
Answer: 因为只要窗口内第一大元素也就是这个 window.front() 在窗口内,那我们可以不用管第二大第三大元素在不在区间内了。因为答案一定是这个第一大元素。如果 window.front() 不在窗口内,则将其弹出,第二个大元素变成第一大元素,第三大元素变成第二大元素以此类推。
代码编写的过程还要时刻检查队列是否为空防止抛出异常。
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n=nums.size();
vector<int> result;
if(n==0) return result;
deque<int> KWindow;
for(int i=0;i<k;i++){
while(KWindow.size()>0&&nums[i]>nums[KWindow.back()]){
KWindow.pop_back();
}
KWindow.push_back(i);
}
result.push_back(nums[KWindow.front()]);
for(int i=k;i<n;i++){
while(KWindow.size()>0&&KWindow.front()<i-k+1){
KWindow.pop_front();
}
while(KWindow.size()>0&&nums[i]>nums[KWindow.back()]){
KWindow.pop_back();
}
KWindow.push_back(i);
result.push_back(nums[KWindow.front()]);
}
return result;
}