文章目录
一、理论
1. 栈
后进先出
//利用双端队列实现
Deque<> stack = new LinkedList<>();
//双端队列实现栈的常用方法
stack.push()
stack.pop()
stack.peek()
stack.isEmpty()
2. 队列
先进先出
Queue<> que = new LinkedList<>();
//个人习惯采用第一种
//成功返回true,失败时返回一个特殊值(取决于操作,为NULL或false)
que.offer();
que.poll();
que.peek();
//成功返回true,在操作失败时抛出异常
add(E e):添加一个元素到队尾
remove():获取队首的元素,并从队列中移除
element():获取队首的元素,但不从队列中移除
//
que.isEmpty();
3. 堆(优先级队列)
对于堆(使用PriorityQueue实现):从队头到队尾按从小到大排就是最小堆(小顶堆),
从队头到队尾按从大到小排就是最大堆(大顶堆)—>队头元素相当于堆的根节点
4. 单调栈
通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了。
单调栈刷题总结
二、堆(优先队列)
1. 语法
- 定义
对于堆(使用PriorityQueue实现):从队头到队尾按从小到大排就是最小堆(小顶堆),
从队头到队尾按从大到小排就是最大堆(大顶堆)—>队头元素相当于堆的根节点 - 比较器
PriorityQueue 默认是小根堆,大根堆需要重写比较器(一定要记住相关语法,哪个是大哪个小顶堆)。
可以在 new PriorityQueue<>() 中的参数部分加入比较器。
具体写法是:(v1, v2) -> v2 - v1。
Queue 类的输入是 offer() 方法/add,弹出是 poll() 方法。
T347. 前k个高频元素 (大顶堆) **
- 思路分析
建立个大顶堆,频率次数高的元素在队头,优先出来(有一些题解强调要小顶堆 不理解!)
小技巧:如何遍历一个map集合
getOrDefault方法 - 代码实现
class Solution {
//优先队列实现大顶堆
//getOrDefault() 方法获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值。
public int[] topKFrequent(int[] nums, int k) {
//1.计算值与出现频率
Map<Integer,Integer> map = new HashMap<>();
for(int i=0;i<nums.length;i++){
map.put(nums[i],map.getOrDefault(nums[i],0)+1);
}
//2.创建大顶堆(从大到小),然后把map的值放到里面
//大顶堆 [ [key,count], ... ]
PriorityQueue<int[]> pq = new PriorityQueue<>( (x,y) -> y[1]-x[1]);
//用map.entrySet 把map解析为 entry类型
for(Map.Entry<Integer,Integer> entry:map.entrySet()){
pq.add(new int[]{entry.getKey(),entry.getValue()});
}
//3.存放结果
int[] res = new int[k];
for(int i = 0;i<k;i++){
res[i] = pq.poll()[0];
}
return res;
}
}
面试题 17.14 最小K个数 (小顶堆) *
class Solution {
//小顶堆
public int[] smallestK(int[] arr, int k) {
PriorityQueue<Integer> pq = new PriorityQueue<Integer>((x,y)->(x-y));//小顶堆
for(int i=0;i<arr.length;i++){
pq.offer(arr[i]);//队头到队尾:从小到大
}
int[] res = new int[k];
for(int i=0;i<k;i++){
res[i] = pq.poll();
}
return res;
}
}
面试题 17.09. 第 k 个数
T692. 前k个高频单词
三、栈与队列相互转换
T232. 用栈实现队列
class MyQueue {
Deque<Integer> stackIn;
Deque<Integer> stackOut;
public MyQueue() {
stackIn = new LinkedList<Integer>();
stackOut = new LinkedList<Integer>();
}
public void push(int x) {
stackIn.push(x);
}
public int pop() {
contrustOut();
return stackOut.pop();
}
public int peek() {
contrustOut();
return stackOut.peek();
}
public boolean empty() {
return stackIn.isEmpty() && stackOut.isEmpty();
}
public void contrustOut(){
if(!stackOut.isEmpty()) return;
while(!stackIn.isEmpty()){
stackOut.push(stackIn.pop());
}
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/
T225. 队列实现栈
利用辅助队列来实现
class MyStack {
//queue内置方法
//offer添加元素
//poll返回第一个元素,并删除
//peek返回第一个元素
//核心做法:构造满足栈要求的队列,利用一个辅助队列来对元素排序后,放到目标队列里面去
//画图分析思路较为清晰
Queue<Integer> que1;//用来模拟栈
Queue<Integer> que2;//辅助队列
public MyStack() {
que1 = new LinkedList<>();
que2 = new LinkedList<>();
}
public void push(int x) {
que2.offer(x);
while (!que1.isEmpty()){
que2.offer(que1.poll());//新的队列,符合栈要求的队列
}
Queue<Integer> tempQue = que1;
que1 = que2;//队列1为最终结果
que2 = tempQue;//队列2为空队列
}
public int pop() {
return que1.poll();
}
public int top() {
return que1.peek();
}
public boolean empty() {
return que1.isEmpty();
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/
四、经典简单题
T20. 有效的括号(栈)
class Solution {
public boolean isValid(String s) {
Deque<Character> stack = new LinkedList<>();
if(s.length()==1) return false;
for(int i=0;i<s.length();i++){
char ch = s.charAt(i);
if(ch == ')'){
if(stack.isEmpty() || stack.pop()!='(') return false;
}
else if(ch == ']'){
if(stack.isEmpty() || stack.pop()!='[') return false;
}
else if(ch == '}'){
if(stack.isEmpty() || stack.pop()!='{') return false;
}else{
stack.push(ch);
}
}
return stack.isEmpty();
}
}
T1047. 删除字符串中的所有重复字符(栈)
class Solution {
public String removeDuplicates(String s) {
Deque<Character> stack = new LinkedList<Character>();
stack.push(s.charAt(0));
for(int i=1;i<s.length();i++){
if(stack.isEmpty() || s.charAt(i) != stack.peek()){
stack.push(s.charAt(i));
}else{
stack.pop();
}
}
String str = "";
while(!stack.isEmpty()){
str = stack.pop() + str;
}
return str;
}
}
T150. 逆波兰表达式 **
class Solution {
//遇到数字就入栈
//遇到符号就弹出两个数字 进行处理后压入栈
//Integer.valueOf 将基本类型int转换为包装类型Integer,或者将String转换成Integer
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new LinkedList<Integer>();
for(int i = 0;i<tokens.length;i++){
String s = tokens[i];
if( s.equals("+") ){// leetcode 内置jdk的问题,不能使用==判断字符串是否相等
stack.push(stack.pop() + stack.pop());
}else if(s.equals("-")){
stack.push(-stack.pop()+stack.pop());
}else if (s.equals("*")){
stack.push(stack.pop()*stack.pop());
}else if (s.equals("/")){
int temp1 = stack.pop();
int temp2 = stack.pop();
stack.push(temp2/temp1);
}else{
stack.push(Integer.valueOf(s));
}
}
return stack.pop();
}
}
五、较为复杂的模拟题
1. 计算器系列问题
T224 基本计算器(没有乘除和空格,有 + - 括号)
- 分析:
用一个栈,记录数字和符号 顺序为数字、符号
sign表示符号的正负
遇到左括号 入栈 右括号 出栈
如何把字符串变成数字 - 代码实现
class Solution {
//只有 加减括号
//加减可以单独算,就用一个sign可以完成
//遇到括号才入栈,依次是数字、符号
//右括号就出栈,算出最终结果
public int calculate(String s) {
Deque<Integer> stack = new LinkedList<>();
int sign = 1;//1代表正号 -1代表负号
int len = s.length();
int num = 0;
for(int i=0;i<len;i++){
char c = s.charAt(i);
if(Character.isDigit(c)){
int cur = c - '0';
while(i+1<len && Character.isDigit(s.charAt(i+1))){
cur = cur * 10 + s.charAt(i+1) - '0';
i++;
}
num = num + sign * cur;//只要没遇到符号和括号 就把结果算出来
}else if(c == '+'){
sign = 1;
}else if(c == '-'){
sign = -1;
}else if(c == '('){
stack.push(num);
num = 0;
stack.push(sign);
sign = 1;//先放数字后放符号
}else if(c == ')'){
num = stack.pop() * num + stack.pop();
}
}
return num;
}
}