二刷:
单调栈, 堆排序(借助优先队列), 哈希去重(统计出现频率)
单调栈:一个一个元素(st是单调栈,保存当前元素后比它大的最近的元素的索引)
堆排序
哈希去重(统计出现频率)
一:栈
1.有效的括号
当遇到左括号时,将其对应的右括号入栈;
当遇到右括号时,与栈顶元素比较,如果匹配则将栈顶元素出栈,否则返回 false。
class Solution {
public boolean isValid(String s) {
Deque<Character> stk=new LinkedList<Character>();
char[] ss=s.toCharArray();
for (char c : ss) {
if (c=='(') {
stk.push(')');
}else if (c=='[') {
stk.push(']');
}else if (c=='{') {
stk.push('}');
}else if (stk.isEmpty()||c!=stk.pop()) {
return false ;
}
}
return stk.isEmpty();
}
}
2.最小栈
class MinStack { private Stack<Integer> stack;//正常的栈 1 - 2 - 3 - 0 private Stack<Integer> min_stack;//用来保存最小值 1 - 1 - 1 - 0 /** * 构造函数初始化两个栈 */ public MinStack() { stack =new Stack<>(); min_stack=new Stack<>(); } /** * 将元素压入栈中,并更新最小值栈 */ public void push(int val) { stack.push(val); if (min_stack.isEmpty()) { min_stack.push(val);// 当前最小栈为空时,直接压入当前元素 }else if (min_stack.peek()>val) { min_stack.push(val);// 当前元素小于最小值时,更新最小值栈 }else { min_stack.push(min_stack.peek());// 当前元素不小于最小值时,保持最小值栈不变 } } /** * 从栈中弹出顶部元素 */ public void pop() { stack.pop(); min_stack.pop(); } /** * 获取栈顶元素 */ public int top() { return stack.peek(); } /** * 获取栈中的最小元素 */ public int getMin() { return min_stack.peek(); } }
3.字符串解码
class Solution { public String decodeString(String s) { StringBuilder res=new StringBuilder();//返回值 //每次遍历后,用stack保存当前的res(就一个元素)。 //下次遍历结束后,res 为stack.pop()与 当前遍历的结果temp(局部变量)的拼接 Deque<String> stack_res=new LinkedList<>(); //保存重复次数,注意次数不一定小于10 ,所以用栈保存、 Deque<Integer> stack_time =new LinkedList<>(); int time=0;//保存重复次数,初始为0 for (char c : s.toCharArray()) { if (Character.isDigit(c)) { time=time*10+c-'0';//强转c为数字 }else if (c=='[') { //把之前的数字存进栈里 stack_time.push(time); //把之前的结果res保存到栈里 stack_res.push(res.toString()); time=0;//初始化下一轮计数初始值为0 res=new StringBuilder();//重置为null }else if (c==']') { //循环time(保存在栈里)次保存到中间变量中temp StringBuilder temp=new StringBuilder(); int cur_time=stack_time.pop(); for (int i = 0; i <cur_time ; i++) { temp.append(res); } //拼接 res = new StringBuilder(stack_res.pop()+temp); }else { res.append(c); } } return res.toString(); } }
4.每日温度--单调栈
栈st保存的是 索引下标
st.append(i),入栈的下标
ans为栈顶的下标减去当前元素的下标
class Solution { public int[] dailyTemperatures(int[] temperatures) { Stack<Integer> st=new Stack<>();//保存下标 int L=temperatures.length; int[] ans =new int[L];//返回结果 for (int i = L-1; i >=0; i--) { int t=temperatures[i]; while (!st.isEmpty() && t>=temperatures[st.peek()]) { st.pop();//去除st中比当前元素小的值 } if (!st.isEmpty()) { ans[i]=st.peek()-i; } st.push(i); } return ans; } }
用deque快
5.柱状图中最大的矩形--单调栈
单调栈:
class Solution { public int largestRectangleArea(int[] heights) { int L=heights.length; Deque<Integer> stk_left=new LinkedList<>();//保存左端的小于当前h的最近元素下标 int[] left=new int[L]; Deque<Integer> stk_right=new LinkedList<>();//保存右端的小于当前h的最近元素下标 int[] right=new int[L]; int ans=0;//返回的结果 //构造right for (int i = L-1; i >=0; i--) { int h=heights[i]; while (!stk_right.isEmpty() && h<=heights[stk_right.peek()]) { stk_right.pop(); } // if (!stk_right.isEmpty()) { // right[i]=stk_right.peek(); // } right[i] = stk_right.isEmpty() ? L :stk_right.peek(); stk_right.push(i); } //构造left for (int i = 0; i <L; i++) { int h=heights[i]; while (!stk_left.isEmpty() && h<=heights[stk_left.peek()]) { stk_left.pop(); } // if (!stk_left.isEmpty()) { // left[i]=stk_left.peek(); // } left[i] = stk_left.isEmpty() ? -1 :stk_left.peek(); stk_left.push(i); } //计算每个i上的矩形面积 for (int i = 0; i < L; i++) { int s= heights[i]*(right[i]-left[i]-1); ans=Math.max(ans, s); } return ans; } }
二:堆
1.数组中的第K个最大元素
堆排序
for (int num : nums) { heap.offer(num); if (heap.size()>k) { heap.poll(); //弹出的是第k+1个元素 } }
2.前 K 个高频元素
数组去重:
class Solution { public int[] topKFrequent(int[] nums, int k) { Map<Integer, Integer> map = new HashMap<Integer, Integer>(); //统计各元素出现的次数---去重了 for (int num : nums) { map.put(num, map.getOrDefault(num, 0)+1); } //遍历map,用最小堆保存频率最大的k个元素 // 构建最小堆,map的值比较大小,但存的k PriorityQueue<Integer> pq=new PriorityQueue<>((a,b)->map.get(a)-map.get(b)); //弹出第k+1个元素,最小堆保存频率最大的k个元素 for (Integer key: map.keySet()) { pq.offer(key); if (pq.size()>k) { pq.poll(); } } // 取出最小堆中的元素--pop() int[] ans =new int[k]; int i=0; while (!pq.isEmpty()) { ans[i++]=pq.poll(); } return ans; } }
3.数据流的中位数
类似题目:
求中值
class MedianFinder { private PriorityQueue<Integer> minQ;//最小堆,升序 private PriorityQueue<Integer> maxQ;//最大堆,降序 private Integer m;//中值 public MedianFinder() { minQ=new PriorityQueue<>((a,b)->b-a); maxQ=new PriorityQueue<>((a,b)->a-b); } public void addNum(int num) { if (m==null) { //第一个元素 m=num; return; } if (num>m) minQ.add(num);//加入最小堆 if (num<m) maxQ.add(num);//加入最大堆 //判断最小堆与最大堆的个数差 if (minQ.size()-maxQ.size()>=2) { //最小堆多,栈顶元素为新的m,原来的m给最大堆 //poll最小堆移除一个 maxQ.add(m); m=minQ.poll(); }else if (maxQ.size()-minQ.size()>=2) { //最大堆多,栈顶元素为新的m,原来的m给最小堆 //poll最大堆移除一个 minQ.add(m); m=maxQ.poll(); } } public double findMedian() { if (maxQ.size()==minQ.size()) { return m; }else if (maxQ.size()-minQ.size()==1) { return (m+maxQ.peek())/2.0; }else { return (m+minQ.peek())/2.0; } } }