【数据结构与算法】(四)队列
循环队列 循环队列浪费一个储存空间,是因为要区别空队列和满队列,
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; }