摘要
【面试必刷101】系列blog目的在于总结面试必刷101中有意思、可能在面试中会被考到的习题。总结通用性的解题方法,针对特殊的习题总结思路。既是写给自己复习使用,也希望与大家交流。
【面试必刷101】递归/回溯算法总结I(十分钟理解回溯算法)
【面试必刷101】递归/回溯算法总结II(十分钟刷爆回溯算法题)
【面试必刷101】链表
【面试必刷101】二叉树
【面试必刷101】二分查找
文章目录
1 基础知识
1.1 栈和队列的性质
- 栈:先进后出
- 队列:先进后出
看图比较好理解:
1.2 java中Deque类做栈和队列
Deque有三种用途:
- 普通队列(一端进另一端出):
Queue queue = new LinkedList()或Deque deque = new LinkedList() - 双端队列(两端都可进出)
Deque deque = new LinkedList() - 堆栈
Deque deque = new LinkedList()
Queue方法 | 等效Deque方法 |
---|---|
add(e) | addLast(e) |
offer(e) | offerLast(e) |
remove() | removeFirst() |
poll() | pollFirst() |
element() | getFirst() |
peek() | peekFirst() |
queue就是从尾部添加元素,头部出来元素。
堆栈方法 | 等效Deque方法 |
---|---|
push(e) | addFirst(e) |
pop() | removeFirst() |
peek() | peekFirst() |
stack就是从头部进行处理,全部都是头部的操作。
1.3 优先队列
// 小->大排序,出来的是最小的
PriorityQueue<Integer> queue2 = new PriorityQueue<Integer>((o1, o2) -> o1 - o2);
// 大->小排序,出来的是最大的
PriorityQueue<Integer> queue1 = new PriorityQueue<Integer>((o1, o2) -> o2 - o1);
-
add() - 将指定的元素插入队列。如果队列已满,则会引发异常。
-
offer() - 将指定的元素插入队列。如果队列已满,则返回false。
-
remove() - 从队列中删除指定的元素
-
poll() - 返回并删除队列的开头
其他方法 | 含义 |
---|---|
size() | 返回优先级队列的长度。 |
toArray() | 将优先级队列转换为数组,并返回它。 |
contains(element) | 在优先级队列中搜索指定的元素。如果找到该元素,则返回true,否则返回false。 |
2 面试必刷习题
2.1 包含min函数的栈
特点就是用两个栈来进行存储。一个记录当前位置的最小值,这样做的好处是相当于记录了每个位置的最小值。
import java.util.*;
import java.util.Stack;
public class Solution {
// 用一个min栈存储最小值
// 用一个栈存储元素
Deque<Integer> dq = new LinkedList<>();
Deque<Integer> minDq = new LinkedList<>();
public void push(int node) {
if (minDq.isEmpty()) {
minDq.addLast(node);
} else if (minDq.peekLast() < node) {
minDq.addLast(minDq.peekLast());
} else {
minDq.addLast(node);
}
dq.addLast(node);
}
public void pop() {
dq.pollLast();
minDq.pollLast();
}
public int top() {
return dq.peekLast();
}
public int min() {
return minDq.peekLast();
}
}
2.2 用栈实现队列
特点就是两个栈来实现的,往里加,一个取数据。取数据如果是空,则从加数据栈里面取。
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if(stack2.empty()){
//为空:将stack1的内容倒回给stack2
while(!stack1.empty()){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
2.3 滑动窗口的最大值
经典习题:滑动窗口的单调队列。
import java.util.*;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size) {
ArrayList<Integer> res = new ArrayList<>();
if (num.length < 3) return res;
// 注意这个dq存的是下标
Deque<Integer> dq = new LinkedList<>();
for (int i = 0; i < num.length; i++) {
if (!dq.isEmpty() && i - dq.peekFirst() >= size) dq.pollFirst();
while (!dq.isEmpty() && num[dq.peekLast()] < num[i]) {
dq.pollLast();
}
dq.addLast(i);
if (i >= size - 1) {
res.add(num[dq.peekFirst()]);
}
}
return res;
}
}
2.4 单调栈
单调栈只有一种应用情况: 找出每个数左边离它最近的比它大/小的数
维护了一个单调递增的栈,怎么理解呢?
如果n位置出现了一个比m位置更小的数(m<n),那么在k位置(k>n)是不需要去判断m位置的,因为最差情况也是n位置了。所以说是维护了一个单调递增的栈去保持这种关系,可以减少判断。
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = in.nextInt();
}
StringBuilder sb = new StringBuilder();
Deque<Integer> dq = new LinkedList<>();
for (int i = 0; i < n; i++) {
while (!dq.isEmpty() && dq.peekFirst() >= arr[i]) {
dq.pollFirst();
}
if (dq.isEmpty()) {
sb.append("-1 ");
} else {
sb.append(dq.peekFirst() + " ");
}
dq.addFirst(arr[i]);
}
System.out.println(sb.toString());
}
}
2.5 最小的K个数
优先队列的语法和使用
import java.util.*;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
PriorityQueue<Integer> pq = new PriorityQueue<>((a, b) -> b - a);
for (int i = 0; i < input.length; i++) {
if (pq.size() < k) {
pq.add(input[i]);
} else {
if (!pq.isEmpty() && pq.peek() > input[i]) {
pq.poll();
pq.add(input[i]);
}
}
}
ArrayList<Integer> res = new ArrayList<Integer>();
while (pq.size() > 0) {
res.add(pq.poll());
}
Collections.reverse(res);
return res;
}
}
2.6 表达式求值
这道题应该不会考,因为代码量太多了。
3 知识点总结
这里有两道题还有点问题:
- 寻找第K大:快排+二分的思路没办法处理
- 表达式求值:比较复杂咋就不写了
其他题目:
- 包含min函数的栈:辅助栈
- 栈实现队列:辅助栈
- 滑动窗口的最大值:有序队列
- 单调栈:有序栈
- 最小的k个数:优先队列
可以看到,栈可以辅助一些数据结构,做到最小值和最大值的判别之类的。同时,单调栈和单调队列一般认为是比较难的,但是只有几道典型例题,多刷刷很好理解:
- 单调栈:找出每个数左边离它最近的比它大/小的数
- 单调队列:找出滑动窗口中的最大值/最小值
基本上单调栈和单调队列只有上面的习题了。
4 总结
慢慢来,孰能生巧。