150. 逆波兰表达式求值
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> st = new Stack<>();
int i1, i2, res;
for(String s : tokens){
if("+".equals(s)){
i2 = st.pop();
i1 = st.pop();
st.push(i1+i2);
}else if("-".equals(s)){
i2 = st.pop();
i1 = st.pop();
st.push(i1-i2);
}else if("*".equals(s)){
i2 = st.pop();
i1 = st.pop();
st.push(i1*i2);
}else if("/".equals(s)){
i2 = st.pop();
i1 = st.pop();
st.push(i1/i2);
}else{
st.push(Integer.valueOf(s));
}
}
return st.pop();
}
}
239. 滑动窗口最大值 (难:单调队列,不同于优先级队列)
此处的单调队列:队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。
设计单调队列的时候,pop,和push操作要保持如下规则:
- pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
- push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止
一开始的思路,维护一个最大值int max;
,但是一旦最大值poll
之后,就不知道此时窗口内的最大值是多少了。
难道要重新遍历一遍?用两个队列?写不下去……
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
LinkedList<Integer> q = new LinkedList<>();
int[] res = new int[nums.length - k + 1];
int max = Integer.MIN_VALUE;
for(int i=0; i<k; i++){
q.offer(nums[i]);
max = max>nums[i]?max:nums[i];
}
res[0] = max;
for(int i=1; i<nums.length - k + 1; i++){
if(max)
}
}
}
使用单调队列
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
// Deque<Integer> q = new LinkedList<>();
Que q = new Que();
int[] res = new int[nums.length-k+1];
// 初始化
int start=0, end=k-1; // 左闭右闭
for(int i=start; i<=end; i++) q.offer(nums[i]);
res[start] = q.peek();
// 窗口开始滑动
start++; end++;
for(; end<nums.length; start++, end++){ // 先出队还是先入队? 先入队,保证队列里会有元素。
// 入队
q.offer(nums[end]);
// 出队
q.poll(nums[start-1]);
// 记录最大值
res[start] = q.peek();
}
return res;
}
private static class Que{
private Deque<Integer> q = new LinkedList<>();
public void offer(int i){
while(!q.isEmpty()){
if(q.peekLast()>=i){
q.offer(i);
break;
}
q.pollLast();
}
if(q.isEmpty()){
q.offer(i);
}
}
public Integer poll(int i){
if(q.peek()==i){
return q.poll();
}
return null;
}
public Integer peek(){
return q.peek();
}
public Integer peekLast(){
return q.peekLast();
}
public boolean isEmpty(){
return q.isEmpty();
}
}
}
代码随想录解法:
- add更加简洁:将while中的if判断放在while中;
- poll更合理:我的poll耦合度太高,并未考虑队列中没有元素的情况。
//解法一
//自定义数组
class MyQueue {
Deque<Integer> deque = new LinkedList<>();
//弹出元素时,比较当前要弹出的数值是否等于队列出口的数值,如果相等则弹出
//同时判断队列当前是否为空
void poll(int val) {
if (!deque.isEmpty() && val == deque.peek()) {
deque.poll();
}
}
//添加元素时,如果要添加的元素大于入口处的元素,就将入口元素弹出
//保证队列元素单调递减
//比如此时队列元素3,1,2将要入队,比1大,所以1弹出,此时队列:3,2
void add(int val) {
while (!deque.isEmpty() && val > deque.getLast()) {
deque.removeLast();
}
deque.add(val);
}
//队列队顶元素始终为最大值
int peek() {
return deque.peek();
}
}
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums.length == 1) {
return nums;
}
int len = nums.length - k + 1;
//存放结果元素的数组
int[] res = new int[len];
int num = 0;
//自定义队列
MyQueue myQueue = new MyQueue();
//先将前k的元素放入队列
for (int i = 0; i < k; i++) {
myQueue.add(nums[i]);
}
res[num++] = myQueue.peek();
for (int i = k; i < nums.length; i++) {
//滑动窗口移除最前面的元素,移除是判断该元素是否放入队列
myQueue.poll(nums[i - k]);
//滑动窗口加入最后面的元素
myQueue.add(nums[i]);
//记录对应的最大值
res[num++] = myQueue.peek();
}
return res;
}
}
347.前 K 个高频元素 (难:优先级队列)
大顶堆(Max Heap)和小顶堆(Min Heap)是堆的两种主要形式,它们的主要区别在于堆的性质(即堆序性质)不同。堆是一种特殊的完全二叉树,通常用数组来实现。
大顶堆是一种特殊的完全二叉树,其中每个节点的值都大于或等于其子节点的值。因此,大顶堆的根节点(堆顶)是所有节点中最大的。
小顶堆是一种特殊的完全二叉树,其中每个节点的值都小于或等于其子节点的值。因此,小顶堆的根节点(堆顶)是所有节点中最小的。
如果使用大顶堆
- PriorityQueue需要传入比较器
- Map.Entry
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> record = new HashMap<>();
for(int i:nums){
record.put(i, record.getOrDefault(i, 0)+1);
}
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b)->(b[1]-a[1]));
for(Map.Entry<Integer, Integer> entry : record.entrySet()){
pq.offer(new int[]{entry.getKey(), entry.getValue()});
}
int[] res = new int[k];
for(int i=0; i<k; i++){
res[i] = pq.poll()[0];
}
return res;
}
}
本题使用小顶堆:因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。