队列概念
队列这个概念非常好理解。你可以把它想象成排队买票,先来的先买,后来的人只能站末尾,不允许插队。先进者先出,这就是典型的“队列”
队列相关的必会代码实现:
-
用数组实现一个顺序队列
public class ArrayQueueT { private int [] queue; private int front; private int rear; private int capacity; public ArrayQueueT(int capacity) { this.capacity = capacity; queue = new int[capacity]; front = 0; rear = -1; } public void enqueue(int item){ if(rear ==capacity-1){ throw new RuntimeException("queue is full"); } rear ++; queue[rear] = item; } public int dequeue(){ if(isEmpty()){ throw new RuntimeException("queue is empty"); } int item = queue[front]; front++; if(front > rear){ front = 0; rear = 1; } return item; } public int peek(){ if(isEmpty()){ throw new RuntimeException("queue is empty"); } return queue[front]; } public int size(){ return rear - front + 1; } public boolean isEmpty(){ return rear == -1 || front >rear; }}
-
用链表实现一个链式队列
public class CircularQueue { private int[] queue; private int head; private int tail; private int capacity; public CircularQueue(int capacity) { queue = new int[capacity]; head = 0; tail = 0; this.capacity = capacity; } public void enqueue(int value) { if (isFull()) { throw new RuntimeException("queue is full"); } tail = (tail + 1) % capacity; queue[tail] = value; } public int dequeue() { if (isEmpty()) { throw new RuntimeException("queue is empty"); } int value = queue[head]; head = (head + 1) % capacity; return value; } private boolean isFull() { return (tail + 1) % capacity == head; } private boolean isEmpty() { return head == tail; } public int size() { if (isEmpty()) { return 0; } if (head < tail) { return tail - head; } return capacity - head + tail; } }
-
实现一个循环队列
public class CircularQueue { private int[] queue; private int head; private int tail; private int capacity; public CircularQueue(int capacity) { queue = new int[capacity]; head = 0; tail = 0; this.capacity = capacity; } public void enqueue(int value) { if (isFull()) { throw new RuntimeException("queue is full"); } tail = (tail + 1) % capacity; queue[tail] = value; } public int dequeue() { if (isEmpty()) { throw new RuntimeException("queue is empty"); } int value = queue[head]; head = (head + 1) % capacity; return value; } private boolean isFull() { return (tail + 1) % capacity == head; } private boolean isEmpty() { return head == tail; } public int size() { if (isEmpty()) { return 0; } if (head < tail) { return tail - head; } return capacity - head + tail; }}
1,设计循环双端队列
题目:
实现 MyCircularDeque 类:
-
MyCircularDeque(int k):构造函数,双端队列最大为k。 -
boolean insertFront():将一个元素添加到双端队列头部。 如果操作成功返回true,否则返回false。 -
boolean insertLast():将一个元素添加到双端队列尾部。如果操作成功返回true,否则返回false。 -
boolean deleteFront():从双端队列头部删除一个元素。 如果操作成功返回true,否则返回false。 -
boolean deleteLast():从双端队列尾部删除一个元素。如果操作成功返回true,否则返回false。 -
int getFront()):从双端队列头部获得一个元素。如果双端队列为空,返回-1。 -
int getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回-1。 -
boolean isEmpty():若双端队列为空,则返回true,否则返回false。 -
boolean isFull():若双端队列满了,则返回true,否则返回false。class MyCircularDeque { private int[] deque; private int front; private int last; private int capacity; public MyCircularDeque(int k) { deque = new int[k+1]; front = 0; last = 0; this.capacity = k+1; } public boolean insertFront(int value) { if (isFull()) { return false; } front= (front -1 +capacity) % capacity; deque[front] = value; return true; } public boolean insertLast(int value) { if (isFull()) { return false; } deque[last] = value; last = (last + 1) % capacity; return true; } public boolean deleteFront() { if (isEmpty()) { return false; } front = (front + 1) % capacity; return true; } public boolean deleteLast() { if (isEmpty()) { return false; } last = (last-1+capacity) %capacity; return true; } public int getFront() { if (isEmpty()) { return -1; } return deque[front]; } public int getRear() { if (isEmpty()) { return -1; } return deque[(last-1+capacity) %capacity]; } public boolean isEmpty() { return last == front; } public boolean isFull() { return (last +1) % capacity == front; }}
2,滑动 窗口最大值
题目:
给你一个整数数组 nums,有一个大小为 k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回
滑动窗口中的最大值
。
题解:优先队列
什么是优先队列:
优先队列(Priority Queue)是一种特殊的队列数据结构,它可以根据元素的优先级进行插入和删除操作。与普通队列不同,优先队列中的元素并不是按照插入的顺序进行访问,而是根据元素的优先级进行排序。
优先队列可以用于解决许多与优先级相关的问题,例如找到最大或最小的元素,按照优先级处理任务等。它提供了以下两个主要操作:
-
插入操作:将一个元素插入到优先队列中。插入操作会根据元素的优先级将元素放置在合适的位置,以保持队列的有序性。
-
删除操作:从优先队列中移除并返回优先级最高(或最低)的元素。删除操作会将队列中的头部元素(优先级最高的元素)移除,并返回该元素。
对于「最大值」,我们可以想到一种非常合适的数据结构,那就是优先队列(堆),其中的大根堆可以帮助我们实时维护一系列元素中的最大值。
对于本题而言,初始时,我们将数组 nums的前 k个元素放入优先队列中。
每当我们向右移动窗口时,我们就可以把一个新的元素放入优先队列中,此时堆顶的元素就是堆中所有元素的最大值。
然而这个最大值可能并不在滑动窗口中,在这种情况下,这个值在数组 nums中的位置出现在滑动窗口左边界的左侧。因此,当我们后续继续向右移动窗口时,这个值就永远不可能出现在滑动窗口中了,我们可以将其永久地从优先队列中移除。 我们不断地移除堆顶的元素,直到其确实出现在滑动窗口中。此时,堆顶元素就是滑动窗口中的最大值。
为了方便判断堆顶元素与滑动窗口的位置关系,我们可以在优先队列中存储二元组 (num,index),表示元素 num 在数组中的下标为 index。
public int[] maxSlidingWindowTest(int [] nums,int k){ int n= nums.length; if(n==0){ return new int[0]; } int [] ans; if(k<n){ ans = new int [n-k+1]; }else{ ans = new int [1]; } PriorityQueue<int []> pq = new PriorityQueue<>(((o1, o2) -> o1[0] != o2[0] ? o2[0]-o1[0]:o2[1]-o1[1])); for(int i=0;i<k;i++){ pq.offer(new int[]{nums[i],i}); } ans[0] = pq.peek()[0]; for(int i=k;i<n;i++){ pq.offer(new int[]{nums[i],i}); while(pq.peek()[1] < i-k){ pq.poll(); } ans[i-k+1] = pq.peek()[0]; } return ans;}