232 用栈实现队列
class MyQueue {
//12-24
Stack<Integer> s1 = new Stack<>();
Stack<Integer> s2 = new Stack<>();
int size;
public MyQueue() {
size = 0;
}
public void push(int x) {
s1.push(x);
size++;
}
public int pop() {
for(int i=0;i<size;i++){
s2.push(s1.pop());
}
size--;
int res = s2.pop();
//最后要保证数据都在s1里
for(int i=0;i<size;i++){
s1.push(s2.pop());
}
return res;
}
public int peek() {
for(int i=0;i<size;i++){
s2.push(s1.pop());
}
int res = s2.peek();
//最后要保证数据都在s1里
for(int i=0;i<size;i++){
s1.push(s2.pop());
}
return res;
}
public boolean empty() {
if(size==0) return true;
else return false;
}
}
/**
* 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();
*/
思路很简单,就是用栈s1存,然后要取值再全部倒到s2,中。一开始没过是因为pop和peek完没有再把s2中得数据倒到s1中。
觉得我这种写法还是不简洁,再pop和peek的操作中有大量重复的,而且不一定每次都要让所有元素都在s1里,每次pop的时候如果s1有元素,直接pops1里的,如果没有再倒元素过去,更新了一下更简洁的代码
class MyQueue {
Stack<Integer> in = new Stack<>();
Stack<Integer> out = new Stack<>();
public MyQueue() {
}
public void push(int x) {
in.push(x);
}
public int pop() {
dumpStackIn();
return out.pop();
}
public int peek() {
dumpStackIn();
return out.peek();
}
public boolean empty() {
return in.empty()&&out.empty();
}
public void dumpStackIn(){
if(!out.empty()) return;
while(!in.empty()){
out.push(in.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();
*/
225 用队列实现栈
本来以为和上一题是一样的,写完才发现不对劲,因为队列一直是先进先出,用几个队列倒过来倒过去都不会改变数据的顺序,所以用一个队列就够了。
只要让每次push进来的元素都处于队列的头即可。
class MyStack {
Queue<Integer> q = new LinkedList<>();
public MyStack() {
}
public void push(int x) {
q.offer(x);
int size = q.size();
//让每次新进来的元素都处于队列的头,其他的元素放到末尾,一个队列本身就能实现
while(size-- >1){
q.offer(q.poll());
}
}
public int pop() {
return q.poll();
}
public int top() {
return q.peek();
}
public boolean empty() {
return q.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();
*/
20 有效的括号
很简单,经典的用栈的做法,只要判断是不是左括号多,或者右括号多,或者和栈顶是否匹配即可
class Solution {
public boolean isValid(String s) {
//53-59
Stack<Character> st = new Stack<>();
for(int i=0;i<s.length();i++){
if(s.charAt(i)=='('||s.charAt(i)=='{'||s.charAt(i)=='['){
st.push(s.charAt(i));
}
else if(st.empty()) return false;//右括号多
else if(s.charAt(i)==')'&&st.pop()!='(') return false;
else if(s.charAt(i)==']'&&st.pop()!='[') return false;
else if(s.charAt(i)=='}'&&st.pop()!='{') return false;
}
if(!st.empty()) return false;//左括号多
return true;
}
}
1047. 删除字符串中的所有相邻重复项
class Solution {
public String removeDuplicates(String s) {
//10
Stack<Character> st = new Stack<Character>();
// st.push(s.charAt(0));
for(int i=0;i<s.length();i++){
if(st.empty()||s.charAt(i)!=st.peek()) st.push(s.charAt(i));
else st.pop();
}
StringBuilder res = new StringBuilder();
while(!st.empty()){
res.append(st.pop());
}
res.reverse();
return res.toString();
}
}
看了题解之后发现其实不用定义Stack,直接用stringBuilder
150. 逆波兰表达式求值
class Solution {
public int evalRPN(String[] tokens) {
//56-10
Stack<Integer> s = new Stack<>();
for(int i=0;i<tokens.length;i++){
if(tokens[i].equals("+")||tokens[i].equals("-")||tokens[i].equals("*")||tokens[i].equals("/")){
int b = s.pop();
int a = s.pop();
switch(tokens[i]){
case "+":{
s.push(a+b);
break;
}
case "-":{
s.push(a-b);
break;
}
case "*":{
s.push(a*b);
break;
}
case "/":{
s.push(a/b);
break;
}
}
}
else s.push(Integer.parseInt(tokens[i]));
}
return s.pop();
}
}
比表达式求值简单多了,只要判断来了运算符,就从栈里pop出两个元素出来计算,先入栈的那个元素是第一个数字,在-和/的时候要注意。第一次写踩的坑是case没有break,导致下面的case即使不满足也计算了,记得要写break!或者不熟练直接用if更简洁。
239 滑动窗口的最大值
因为写过一次,只记得用双端队列,但是具体怎么做还是忘了,总的思想就是每次从右边进队列的时候要把队列中的右边的元素比当前元素小的都poll出去,因为他们再也用不到了,这样维护出来的队列是非递增的,只要再从队头判断下标在窗口内的就可以
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
//45
ArrayDeque<Integer> q = new ArrayDeque<Integer>();
// ArrayList<Integer> list = new ArrayList<Integer>();
int[] res = new int[nums.length-k+1];
int index = 0;
for(int i = 0;i<nums.length;i++){
//把队尾的小于当前元素的都poll
while(!q.isEmpty()&&nums[q.peekLast()]<nums[i]) q.pollLast();
q.offerLast(i);
//判断在窗口内的
while(q.peekFirst()+k-1<i) q.pollFirst();
if(i+1>=k)
//list.add(nums[q.peekFirst()]);
res[index++] = nums[q.peekFirst()];
}
// int[] res = new int[list.size()];
// for(int i=0;i<list.size();i++){
// res[i] = list.get(i);
// }
return res;
}
}
值得注意的是一直习惯用list存,然后再用int数组倒过来,但是这题可以明确知道结果数组的长度,所以可以直接用int数组,而不用再开list
347 前K个高频元素
疯狂打星号
第一反应就是用map存频率,然后对map的value排序,发现不知道怎么排序,而且这样的时间复杂度是O(nlogn)不符合要求。思路是用优先队列!用小顶堆,每次进来一个新元素判断和堆顶(最小的)谁大,如果大于堆顶就offer进来,把堆顶poll出去
set的遍历还是要注意!!优先队列存一个数组就行!用map就写得很复杂
class Solution {
public int[] topKFrequent(int[] nums, int k) {
//44
//map 存 然后对value排序
Map<Integer,Integer> p = new HashMap<>();
for(int i=0;i<nums.length;i++){
p.put(nums[i],p.getOrDefault(nums[i],0)+1);
}
//PriorityQueue<Map.Entry<Integer, Integer>> pq = new PriorityQueue<>((a, b) -> a.getValue() - b.getValue());
PriorityQueue<int[]> pq = new PriorityQueue<>((pair1,pair2)->pair1[1]-pair2[1]);
for(Map.Entry<Integer,Integer> entry:p.entrySet()){
if(pq.size()<k) pq.offer(new int[]{entry.getKey(),entry.getValue()});
else{
if(pq.peek()[1]<entry.getValue()){
pq.poll();
pq.offer(new int[]{entry.getKey(),entry.getValue()});
}
}
}
int[] res = new int[k];
//poll()顺序出来的是从小到大
for(int i=k-1;i>=0;i--){
res[i] = pq.poll()[0];
}
return res;
}
}
优先队列插入和删除的时间复杂度是(logk)k是队列里的元素数量,<n,所以满足题目要求