[leetcode刷题]——栈和队列
此篇博客主要整理栈和队列的问题。
一、用栈实现队列
232、用栈实现队列 (easy) 2021-01-15
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列的支持的所有操作(push、pop、peek、empty):
实现 MyQueue 类:
void push(int x) 将元素 x 推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek() 返回队列开头的元素
boolean empty() 如果队列为空,返回 true ;否则,返回 false
class MyQueue { //数据存放在1中,需要取出或者看时转移到2中进行,然后放回1中 private Stack<Integer> stack1; private Stack<Integer> stack2; /** Initialize your data structure here. */ public MyQueue() { stack1 = new Stack<Integer>(); stack2 = new Stack<Integer>(); } /** Push element x to the back of queue. */ public void push(int x) { stack1.push(x); } /** Removes the element from in front of queue and returns that element. */ public int pop() { while(!stack1.empty()){ stack2.push(stack1.pop()); } int pop = stack2.pop(); while(!stack2.empty()){ stack1.push(stack2.pop()); } return pop; } /** Get the front element. */ public int peek() { while(!stack1.empty()){ stack2.push(stack1.pop()); } int peek = stack2.peek(); while(!stack2.empty()){ stack1.push(stack2.pop()); } return peek; } /** Returns whether the queue is empty. */ public boolean empty() { return stack1.empty(); } }
二、使用队列来实现栈
225.使用队列来实现栈 (easy) 2021-01-15
使用队列实现栈的下列操作:
push(x) -- 元素 x 入栈
pop() -- 移除栈顶元素
top() -- 获取栈顶元素
empty() -- 返回栈是否为空
需要注意的是,Java定义有Stack类,可以new。一共有五个方法,分别是:empty(),peek(),pop(),push(),search()。
但是java中的Queue定义的是接口interface,不能直接new。实现此接口的有LinkedList等等。此接口有几常用个方法,主要是这三个,add( ), peek( ), pull( ) 。
class MyStack { //add( ), peek( ), poll( ) private Queue<Integer> queue1; private Queue<Integer> queue2; //1用来存放数据,2用来操作 /** Initialize your data structure here. */ public MyStack() { queue1 = new LinkedList<>(); queue2 = new LinkedList<>(); } /** Push element x onto stack. */ public void push(int x) { queue1.add(x); } /** Removes the element on top of the stack and returns that element. */ public int pop() { while(queue1.size() > 1){ queue2.add(queue1.poll()); } int pop = queue1.poll(); while(!queue2.isEmpty()){ queue1.add(queue2.poll()); } return pop; } /** Get the top element. */ public int top() { while(queue1.size() > 1){ queue2.add(queue1.poll()); } int top = queue1.peek(); queue2.add(queue1.poll()); while(!queue2.isEmpty()){ queue1.add(queue2.poll()); } return top; } /** Returns whether the stack is empty. */ public boolean empty() { return queue1.isEmpty(); } }
三、最小栈
155、最小栈 (easy) 2021-01-15
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) —— 将元素 x 推入栈中。
pop() —— 删除栈顶的元素。
top() —— 获取栈顶元素。
getMin() —— 检索栈中的最小元素。
偷了个懒,使用上一题用队列实现栈,然后调用Collections.min()函数。好家伙,直接时间、内存都是5%,怕了怕了。
我最初的想法是,设置一个变量记录栈的最小值。但是弹出操作后,有可能会将最小值弹出,然后又得重新遍历整个栈。所以直接用两个队列来存储数据,方便弹出后取最小值。
力扣评论区真的卧虎藏龙,有位朋友实现了我最初的想法。简单来说就是最小值入栈时将当前最小值也入栈存储,最小值出栈时连出两个元素。
具体来说就是:在入栈的时候,如果是比最小值大,那么直接入栈;如果比最小值还小,那么先将当前最小值(也就是次小值)入栈,然后将此最小值入栈,最小值跟新。在出栈的时候,如果出的不是最小值,那么直接出栈;如果出栈的等于最小值,那么将最小值赋值为栈顶第二个元素(也就是次小值),出栈两个元素。
class MinStack { private int min = Integer.MAX_VALUE; private Stack<Integer> stack; /** initialize your data structure here. */ public MinStack() { stack = new Stack<>(); } public void push(int x) { if(min >= x){ stack.push(min); min = x; } stack.push(x); } public void pop() { if(stack.pop() == min){ min = stack.pop(); } } public int top() { return stack.peek(); } public int getMin() { return min; }}
四、有效的括号
20.有效的括号 (easy) 2021-01-16
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。示例 1:
输入: "()[]{}" 输出: true
示例 2:
输入: "([)]" 输出: false
此题解法:核心思想就是使用栈。左括号入栈,右括号出栈对比。
class Solution { public boolean isValid(String s) { char[] arr = s.toCharArray(); Map<Character,Character> map = new HashMap<>(); map.put('(', ')'); map.put('{', '}'); map.put('[', ']'); Stack<Character> stack = new Stack<>(); if(arr.length % 2 != 0){ return false; } for(int i = 0; i < arr.length; i++){ if(arr[i] == '(' || arr[i] == '[' || arr[i] == '{'){ stack.push(arr[i]); } if(arr[i] == ')' || arr[i] == ']' || arr[i] == '}'){ if(stack.empty() || map.get(stack.pop()) != arr[i]){ return false; } } } if(!stack.empty()){ return false; } return true; } }
总能在力扣评论区看到一堆神人。直接循环遍历字符串,每次至少可以去掉一个括号。美中不足就是有点费时费空间。
class Solution { public boolean isValid(String s) { int length = s.length() / 2; for (int i = 0; i < length; i++) { s = s.replace("()", "").replace("{}", "").replace("[]", ""); } return s.length() == 0; } }
五、数组中元素与下一个比他大的元素之间的距离
739. 每日温度 (medium) 2021-01-16
请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。
最无脑的方法就是,双重循环,哈哈,耗时击败5%。
class Solution { public int[] dailyTemperatures(int[] T) { for(int i = 0 ; i < T.length; i++){ for(int j = i ; j < T.length; j++){ if(T[j] > T[i]){ T[i] = j -i; break; }else{ if(j == T.length - 1){ T[i] = 0; } } } } return T; } }
一个很妙的算法是,新建一个栈一个数组,从头向后遍历,将数组下标放进栈中,下标对应的数应该是从大到小,如果遍历到比栈顶存储下标对应的元素大,那么比当前数小的全部出栈。栈始终保持栈底最大,栈顶最小。
class Solution { public int[] dailyTemperatures(int[] T) { LinkedList<Integer> stack = new LinkedList<>(); //一个栈,存放索引 int[] ans = new int[T.length]; stack.push(0); for(int i = 1; i < T.length; i++){ while(!stack.isEmpty() && T[i] > T[stack.peek()]){ ans[stack.peek()] = i - stack.pop(); } stack.push(i); } return ans; } }
六、循环数组中比当前元素大的下一个元素
503. 下一个更大的元素 (medium) 2021-01-16
给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。
示例 1:
输入: [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
解法:与上一题类似,都是使用单调栈原理。入栈都是从大到小,碰到比栈顶大的元素,小的出栈大的入栈。始终保持栈底最大栈顶最小。此题循环数组就是遍历两次,第二次只出不进,最后留在栈中的全部赋值-1。
class Solution { public int[] nextGreaterElements(int[] nums) { int len = nums.length; int[] ans = new int[len]; Stack<Integer> stack = new Stack<>(); for(int i = 0; i < len; i++){ while(!stack.empty() && nums[i] > nums[stack.peek()]){ ans[stack.pop()] = nums[i]; } stack.push(i); } if(!stack.empty()){ //如果栈不为空,再次遍历,只是这次不入栈了 for(int i = 0; i < len; i++){ while(!stack.empty() && nums[i] > nums[stack.peek()]){ ans[stack.pop()] = nums[i]; } } } while(!stack.empty()){ //如果还是没空,全部赋值-1 ans[stack.pop()] = -1; } return ans; } }