java创建堆栈和操作函数
Queue<String> queue = new LinkedList<String> ();//队列定义
Deque<String> stack= new LinkedList<String>();//堆栈
队列方法:
queue.offer(e) null
queue.poll() 返回移除的值
queue.peek()
堆栈方法:
stack.push(e) null
stack.pop()返回移除的值
stack.peek()返回栈顶的元素但不移除它
stack.size()返回栈中元素2 (假设栈里有1,5两个元素)
stack.search(2));返回从栈顶往前数第2个元素1
堆
堆的逻辑结构是一颗完全二叉树
堆的物理结构是一个数组
大根堆就是整个完全二叉树,任意一个根节点的值都比左右子树的值大
小根堆表示整个完全二叉树,任意一个根节点的值都比左右子树的值小。
leetcode
20. 有效的括号
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
也就是说:括号可以互相包含,但不能参差摆放,如下例: “{[]}()” true ({)} false
遇到左括号入栈
遇到右括号检验栈顶元素是否和它匹配,匹配则出栈,否则False
最后栈空则true
class Solution {
public boolean isValid(String s) {
//括号可以互相包含,但不能参差摆放,如下例: "{[]}()" true ({)} false
Deque<Character> sta = new LinkedList<Character>();
HashMap<Character,Character> map = new HashMap<Character,Character>();
map.put('(',')');
map.put('[',']');
map.put('{','}');
for(int i=0;i<s.length();i++){
if(s.charAt(i)=='('||s.charAt(i)=='['||s.charAt(i)=='{'){
sta.push(s.charAt(i));
}
else if(s.charAt(i)==')'||s.charAt(i)==']'||s.charAt(i)=='}'){
if(!sta.isEmpty() &&map.get(sta.peek())==s.charAt(i)){//匹配上了
sta.pop();
}
else return false;
}
}
return sta.isEmpty();
}
}
155. 最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:
MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。
关键在于:建两个栈,一个存正常放入的数,另一个存小值使得栈顶是当前时刻最小的值。
class MinStack {
Deque<Integer> mstack;
Deque<Integer> stackmin;
int min;
public MinStack() {
mstack = new LinkedList<Integer>();
stackmin = new LinkedList<Integer>();//存min的栈,栈顶是当前时刻最小的值
}
public void push(int val) {
mstack.push(val);
if(!stackmin.isEmpty()){
if(val<=stackmin.peek()){//比栈顶值小就入栈
stackmin.push(val);
}
}
else{
stackmin.push(val);
}
}
public void pop() {
int top = mstack.pop();
if(top==stackmin.peek()){
stackmin.pop();
}
}
public int top() {
return mstack.peek();
}
public int getMin() {
return stackmin.peek();
}
}
394. 字符串解码
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
(重点样例)
输入:s = “3[a2[c]]”
输出:“accaccacc”
输入:s=b2[c]
输出:abcc
重点注意:在什么时候数组字母入栈和出栈
以及动态更新字母串用StringBuilder或StringBuffer
class Solution {
public String decodeString(String s) {
//有嵌套的情况
//数字存放在数字栈,字符串存放在字符串栈,遇到右括号时候弹出一个数字栈,字母栈弹到左括号为止。
//关键点:(1)处理多位数(2)遇到 '[',将当前的字符串和数字推入各自的栈(3)遇到 ']'开始解码
Deque<Integer> numStack = new LinkedList<Integer>();
Deque<String> strStack = new LinkedList<String>();
int number=0;
String str="";
for(int i=0;i<s.length();i++){
if(Character.isDigit(s.charAt(i))){//是数字
number = number*10+s.charAt(i)-'0';//处理多位数
}
else if(s.charAt(i)=='['){//数字结束,字母开始
numStack.push(number);
number=0;
strStack.push(str);
str="";
}
else if(s.charAt(i)==']'){//字母结束,处理一次数字加字母
StringBuilder strb = new StringBuilder(strStack.pop());//用str栈顶的初始化一个stringbuilder
int n=numStack.pop();
for(int j=0;j<n;j++){
strb.append(str);
}
str=strb.toString();//更新当前字符串
}
else{//是字母
str+=s.charAt(i);
}
}
return str;
}
}
739. 每日温度
给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。
示例 1:
输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]
示例 2:
输入: temperatures = [30,40,50,60]
输出: [1,1,1,0]
示例 3:
输入: temperatures = [30,60,90]
输出: [1,1,0]
单调栈:在单调栈基础题中,经常需要类似这种的解题思路:在 O(n) 的时间复杂度内求出数组中各个元素右侧第一个更大的元素及其下标,然后一并得到其他信息。
下图摘自leetcode:
栈中存入下标,并维持栈中下标对应的元素从左到右非递增。
arr[0],arr[1],arr[2]非递增依次入栈
arr[2]出栈,arr[3]入栈,以此同时记录result[2]=3-2;(这个result是题目要求的下一个更大元素)
arr[3],arr[1],arr[0]都出栈,并记录result[3]=4-3,result[1]=4-1,result[0]=4-0;
至于右侧没有更大元素的在result数组里没填过值,就默认为0了。【而按照下一题,HashMap里没有默认值,所以是map.containsKey为空判断一下然后取值-1】
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
//单调栈
Deque<Integer> decStack = new LinkedList<Integer>();
int[] result = new int[temperatures.length];
for(int i=0;i<temperatures.length;i++){
while(!decStack.isEmpty()&&temperatures[i]>temperatures[decStack.peek()]){
int m = decStack.pop();
result[m]=i-m;//记录差距填数
}
decStack.push(i);//记录下标
}
return result;
}
}
496. 下一个更大元素 I
nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。
给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。
对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。
返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。
示例 1:
输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
- 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
- 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。
- 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
示例 2:
输入:nums1 = [2,4], nums2 = [1,2,3,4].
输出:[3,-1]
解释:nums1 中每个值的下一个更大元素如下所述: - 2 ,用加粗斜体标识,nums2 = [1,2,3,4]。下一个更大元素是 3 。
- 4 ,用加粗斜体标识,nums2 = [1,2,3,4]。不存在下一个更大元素,所以答案是 -1 。
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
//下一个更大的元素,就是用单调栈解
Deque<Integer> decStack = new LinkedList<Integer>();
int[] result = new int[nums1.length];
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();//因为num1是num2的子集且打乱顺序,就用HashMap记录
for(int i=0;i<nums2.length;i++){
while(!decStack.isEmpty()&&nums2[i]>decStack.peek()){
int m=decStack.pop();
map.put(m,nums2[i]);
}
decStack.push(nums2[i]);//这回是直接记录值
}
for(int i=0;i<nums1.length;i++){
result[i]= map.containsKey(nums1[i])? map.get(nums1[i]):-1;
}
return result;
}
}
215. 数组中的第K个最大元素
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入: [3,2,1,5,6,4], k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4
Arrays.sort(nums) 默认使用快排
- 快排
class Solution {
public int findKthLargest(int[] nums, int k) {
Arrays.sort(nums);//快排
return nums[nums.length-k];
}
}
- 优先队列
优先队列的思路是很朴素的。由于找第 K 大元素,其实就是整个数组排序以后后半部分最小的那个元素。因此,我们可以维护一个有 K 个元素的最小堆:
新遍历到的数大于堆顶的时候,才将堆顶拿出,然后放入新读到的数,进而让堆自己去调整内部结构。
PriorityQueue
底层是动态数组,底层是基于堆实现。
class Solution {
public int findKthLargest(int[] nums, int k) {
//维护一个k大小的小顶堆,直接使PriorityQueue不用手动实现堆操作了
PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();//小顶堆
for(int i=0;i<k;i++){//建大小为k的堆
minHeap.offer(nums[i]);
}
for(int i=k;i<nums.length;i++){//相当于从全部数组里提出len-k个小值就得到了第k大的值
if(nums[i]>minHeap.peek()){//比堆顶大,就提出堆顶小值,把nums[i]放进去
minHeap.poll();
minHeap.offer(nums[i]);
}
}
return minHeap.peek();
}
}
347. 前 K 个高频元素
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
依旧是k大小的小顶堆【优先队列】
重点注意:
用HashMap统计元素出现次数,以及优先队列里元素的存储形式。
还有HashMap的遍历方式
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
//优先队列里元素的存储形式是int[]的数组,[0]存key[1]存value
PriorityQueue<int[]> minHeap = new PriorityQueue<int[]>(n,new Comparator<int[]>(){//定义比较方式
public int compare(int[] a,int[] b){
return a[1]-b[1];//按value升序,即小顶堆
}
} );
整体代码:
class Solution {
public int[] topKFrequent(int[] nums, int k) {
//先统计每个数出现次数,再建k大小的小顶堆
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int i=0;i<nums.length;i++){
if(map.containsKey(nums[i])){
int cn=map.get(nums[i])+1;
map.put(nums[i],cn);
}
else{
map.put(nums[i],1);
}
}
int n=map.size();
//优先队列里元素的存储形式是int[]的数组,[0]存key[1]存value
PriorityQueue<int[]> minHeap = new PriorityQueue<int[]>(n,new Comparator<int[]>(){//定义比较方式
public int compare(int[] a,int[] b){
return a[1]-b[1];//按value升序,即小顶堆
}
} );
int cn=0,kk=0;
//放前k个
for(Integer key:map.keySet()){
cn++;
if(cn>k){
break;
}
int value=map.get(key);
minHeap.offer(new int[]{key,value});
}
int cnn=0;
//继续后n-k个
for(Integer key:map.keySet()){
cnn++;
if(cnn>k){
int value=map.get(key);
if(value>minHeap.peek()[1]){
minHeap.poll();
minHeap.offer(new int[]{key,value});
}
}
}
//最后数组存放k大小堆中的值,不按照顺序
int[] a=new int[k];
for(int i=0;i<k;i++){
System.out.println(minHeap.peek()[0]+":"+minHeap.peek()[1]);
a[i]=minHeap.poll()[0];
}
return a;
}
}