今天进入队列和栈的学习。
1.括号匹配问题(力扣20)
public boolean isValid(String s) { if(s.length()<=1) return false; Stack<Character> stack = new Stack<>(); for (int i = 0; i < s.length(); i++) { if(s.charAt(i)=='('||s.charAt(i)=='['||s.charAt(i)=='{'){ stack.push(s.charAt(i)); } else if(stack.isEmpty()) return false; else if(s.charAt(i) == match(stack.peek())) stack.pop(); else return false; } if(stack.empty()) return true; else return false; } public char match(char c){ if(c=='(') return ')'; else if (c=='[') return ']'; else return '}'; }
2.删除字符串中的所有相邻重复项(力扣1047)
可以把此题当作匹配题做,一旦遇到和栈顶重复的字符,则将字符弹出。
public String removeDuplicates(String s) { //Stack为ArrayDeque的古老实现类 // Stack<Character> stack = new Stack<>(); ArrayDeque<Character> stack = new ArrayDeque<>(); for (int i = 0; i < s.length(); i++) { if(stack.isEmpty()||stack.peek()!=s.charAt(i)) stack.push(s.charAt(i)); else stack.pop(); } StringBuilder sb = new StringBuilder(); while (stack.size()!=0){ sb.append(stack.pop()); } sb.reverse(); return sb.toString(); }
3.逆波兰表达式求值(力扣150)
用栈,遇到数字入栈,遇到操作符则出栈两个元素,用第二个出栈的操作第一个出栈(注意主要是减法和除法),结果继续进栈。最终栈中元素即为结果。
public int evalRPN(String[] tokens) { Deque<Integer> deque = new ArrayDeque<>(); for (int i = 0; i < tokens.length; i++) { if(tokens[i].equals("+")) deque.push(deque.pop()+deque.pop()); else if(tokens[i].equals("-")){ int a = deque.pop(); int b = deque.pop(); deque.push(b-a); } else if(tokens[i].equals("*")) deque.push(deque.pop()*deque.pop()); else if(tokens[i].equals("/")){ int a = deque.pop(); int b = deque.pop(); deque.push(b/a); } else deque.push(Integer.parseInt(tokens[i])); } return deque.pop(); }
*4.滑动窗口最大值(力扣239)
这里第一次引入了单调队列的知识,维护一个队内元素具有单调递增或者单调递减的队列。对于本题需要找到滑动窗口的最大值,采用单调递减队列,我们规定若窗口移除的元素正好是队头元素,则队头元素出队,若窗口加入的元素比队尾元素大,则移除队尾元素,保证队列的单调递减性。
class MyDeque{ Deque<Integer> deque = new LinkedList<>(); public MyDeque(){ } //移除元素 public void poll(int val){ if(!deque.isEmpty() && val == deque.peek()) deque.poll(); } //加入元素 public void offer(int val) { while (!deque.isEmpty() && val > deque.getLast()) { deque.removeLast(); } deque.offer(val); } public int peek(){ return deque.peek(); } } public int[] maxSlidingWindow(int[] nums, int k) { //若滑动窗口移除的是单调队列的出口元素,则弹出队列出口元素; //若滑动窗口加入的元素大于单调队列的入口元素,则弹出队列入口元素 if(nums.length==1) return nums; MyDeque deque = new MyDeque(); int[] res = new int[nums.length-k+1]; int num = 0; for (int i = 0; i < k; i++) { deque.offer(nums[i]); } res[num++] = deque.peek(); for (int i = k; i < nums.length; i++) { //滑动窗口移除最前面的元素时, deque.poll(nums[i - k]); //滑动窗口加入最后面的元素 deque.offer(nums[i]); //记录对应的最大值(此时单调队列的队头) res[num++] = deque.peek(); } return res; }
*5.前k个高频元素(力扣347)
本题采用先统计频数(利用map)再对频数排序的方法可以做,但是时空复杂度都很高。选择采用优先级队列,优先级队列其实是堆,分为大顶堆小顶堆,本题采用小顶堆,将队列中最后k个元素(队列中存放的是entries,我们把根据value排序后的对应的key输出)找到即为答案。
public int[] topKFrequent(int[] nums, int k) { // HashMap<Integer,Integer> hm = new HashMap<>(); // int[] res = new int[k]; // int[] res1 = new int[k]; // for (int i = 0; i < nums.length; i++) { // hm.put(nums[i],hm.getOrDefault(nums[i],0)+1); // } // Object[] arr = hm.values().toArray(); // Arrays.sort(arr); // for (int i = 0; i < k ; i++) { // res[i] = (Integer) arr[arr.length-1-i]; // } // int num = 0; // Iterator iterator = hm.keySet().iterator(); // while (iterator.hasNext()){ // int key = (int)iterator.next(); // for (int i = 0; i < k; i++) { // if(hm.get(key)==res[i]){ // res1[num++] = key; // if(num==k) // return res1; // break; // } // } // } // return res1; //采用优先级队列 HashMap<Integer,Integer> hm = new HashMap<>(); int[] res = new int[k]; for (int i = 0; i < nums.length; i++) { hm.put(nums[i],hm.getOrDefault(nums[i],0)+1); } Set<Map.Entry<Integer,Integer>> entries = hm.entrySet(); //建立小顶堆,若大顶堆则为o2.getValue()-o1.getValue() PriorityQueue<Map.Entry<Integer,Integer>> queue = new PriorityQueue<>((o1, o2) -> o1.getValue() - o2.getValue()); for (Map.Entry<Integer, Integer> entry : entries) { queue.offer(entry); if (queue.size() > k) { //本优先级队列采用的小根堆,取最后k个 queue.poll(); } } for (int i = k - 1; i >= 0; i--) { res[i] = queue.poll().getKey(); } return res; }