先明白栈和队列的区别。
- 栈:先进后出(想成一个桶子,最先放进去的东西被压在底下,需要等它上面的东西拿完才能拿它)
- 提供:push/pop接口等
- 队列:先进先出(排队,排在第一个位置的人,可以先走)
- 提供:add/poll接口等等
232. 用栈实现队列
思路:重点要画图理解!使用两个栈来实现,一开始两个都为空
- push方法:始终交给一个栈s1处理插入
- pop方法:因为现在弹出的顺序是按照先进后出的!所以不符合要求,需要处理一下
- 只要把s1中的元素全部放到s2中,此时s2弹出来的元素顺序其实就跟队列一样了!
- 当s2中的元素都没了,就要继续从s1中把累积的元素放到s2!
- 总结:最后的弹出交给s2处理!
- peek方法:同理pop方法
class MyQueue {
Stack<Integer> s1;
Stack<Integer> s2;
public MyQueue() {
s1=new Stack<>();
s2=new Stack<>();
}
public void push(int x) {
s1.push(x);
}
public int pop() {
if(s2.isEmpty()){
while(!s1.isEmpty()){
s2.push(s1.pop());
}
}
int val=s2.pop();
return val;
}
public int peek() {
if(s2.isEmpty()){
while(!s1.isEmpty()){
s2.push(s1.pop());
}
}
int val=s2.peek();
return val;
}
public boolean empty() {
return s1.isEmpty()&&s2.isEmpty();
}
}
225. 用队列实现栈
思路:跟上面题目类似,但现在是用队列实现栈,依旧需要两个队列。
- push方法:记住最后q2始终为空!最终元素都在q1。那如何加入元素?首先加入到q2中,然后将q1队列都放到q2中。这样弹出顺序就是跟栈是一致的。最后交换两个队列。(保持固定这样下次就不用判断哪个队列为空?要插入哪个队列...等等)
- pop方法:元素都在q1,所以直接操作q1弹出
- top方法:同pop方法
class MyStack {
Queue<Integer> q1;
Queue<Integer> q2;
public MyStack() {
q1=new LinkedList<>();
q2=new LinkedList<>();
}
//最后q2始终为空!最终元素都在q1
public void push(int x) {
q2.add(x);
while(!q1.isEmpty()){
q2.add(q1.poll());
}
Queue<Integer> temp=q1;
q1=q2;
q2=temp;
}
public int pop() {
return q1.poll();
}
public int top() {
return q1.peek();
}
public boolean empty() {
return q1.isEmpty();
}
}
20. 有效的括号
思路:利用stack的先进后出特性,如果遇到左括号则放入对应的右括号。当前字符串如果是右括号,则跟stack中的元素进行比对,如果相同表示凑成一对!不同表示不能凑对是无用的返回false
public boolean isValid(String s) {
Stack<Character> stack=new Stack<>();
for(char ch : s.toCharArray()){
if(ch=='('){
stack.push(')');
}
else if(ch=='['){
stack.push(']');
}else if(ch=='{'){
stack.push('}');
}else{
if(!stack.isEmpty() && stack.peek()==ch)
stack.pop();
else
return false;
}
}
return stack.isEmpty();
}
1047. 删除字符串中的所有相邻重复项
思路:⚠️用的是if!而不是while!如果当前元素和stack中元素相同,于是把stack中元素删除,并且当前元素不会加到stack中(等于同时删除两个,不会加到stack中)。stack中剩余的元素等下次遍历时继续判断是有相同还是没有相同
public String removeDuplicates(String s) {
Stack<Character> stack=new Stack<>();
for(char ch :s.toCharArray()){
if(!stack.isEmpty() && stack.peek()==ch){
stack.pop();
}
else
stack.push(ch);
}
String str=new String();
while(!stack.isEmpty()){
str=stack.pop()+str;
}
return str;
}
150. 逆波兰表达式求值
思路:也是用stack的特性,根据逆波兰表示法,遇到运算符时,肯定已经有2个数字(or以上),放心取出来
public int evalRPN(String[] tokens) {
Stack<Integer> stack=new Stack<>();
for(int i=0;i<tokens.length;i++){
if(tokens[i].equals("+")){
int b=stack.pop();
int a=stack.pop();
stack.push(a+b);
}else if(tokens[i].equals("-")){
int b=stack.pop();
int a=stack.pop();
stack.push(a-b);
}else if(tokens[i].equals("*")){
int b=stack.pop();
int a=stack.pop();
stack.push(a*b);
}else if(tokens[i].equals("/")){
int b=stack.pop();
int a=stack.pop();
stack.push(a/b);
}else
stack.push(Integer.valueOf(tokens[i]));
}
return stack.peek();
}
239. 滑动窗口最大值
思路:滑动窗口在遍历数组时,顺带找出当前滑动窗口范围内最大的元素!用双端队列作为滑动窗口!要注意:
- 队列中元素要排序(单调递减)
- 头部放最大的元素
- 窗口移动的时候,队列需要弹出超过窗口范围的元素
没有现成的,需要我们自己去实现一下这个队列!
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
myQueue queue=new myQueue();
int[] res=new int[nums.length-k+1];
for(int i=0;i<k;i++){
queue.add(nums[i]);
}
//第一个范围
int idx=0;
res[idx++]=queue.peek();
for(int i=k;i<nums.length;i++){
//窗口移动,弹出超出范围的元素
queue.poll(nums[i-k]);
//加入可能的最大元素
queue.add(nums[i]);
res[idx++]=queue.peek();
//窗口继续往下移动
}
return res;
}
class myQueue{
Deque<Integer> queue;
public myQueue(){
queue=new LinkedList<>();
}
public void add(int val){
while(!queue.isEmpty() && queue.getLast()<val){
queue.removeLast();
}
queue.add(val);
}
public void poll(int val){
if(!queue.isEmpty() && queue.peek()==val){
queue.poll();
}
}
public int peek(){
return queue.peek();
}
}
}
思路:
347. 前 K 个高频元素
思路:使用优先级队列,先用map记录每个数的次数,然后将map的entry集合放入到set中!创建一个优先级队列,遍历set中的entry,一个个加入队列中,如果当前队列大小超过k表示要剔除元素,于是把头部元素丢掉(队列是先先进先出),头部元素正好就是当前元素中次数最小的那个。遍历结束就得到前k个高频元素!再放到数组中
public int[] topKFrequent(int[] nums, int k) {
Map<Integer,Integer> map=new HashMap<>();
for(int i:nums){
map.put(i,map.getOrDefault(i,0)+1);
}
Set<Map.Entry<Integer,Integer>> set=map.entrySet();
//次数是小到大的优先队列
PriorityQueue<Map.Entry<Integer,Integer>> queue=new PriorityQueue<>((a,b)->a.getValue()-b.getValue());
for(Map.Entry<Integer,Integer> entry :set){
queue.add(entry);
//超过size,就弹出头部元素(当前队列中次数最小的)
if(queue.size()>k){
queue.poll();
}
}
int[] res=new int[k];
int idx=0;
while(!queue.isEmpty()){
res[idx++]=queue.poll().getKey();
}
return res;
}