java中栈的实现方式:
先进后出
Stack<Integer> st = new Stack<Integer>();
队列:
常用LinkedList 集合,它实现了Queue 接口和List接口;
LinkedList底层是一个双向链表
队列主要分为阻塞和非阻塞,有界和无界、单向链表和双向链表之分;
add 增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常
remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
offer 添加一个元素并返回true 如果队列已满,则返回false
poll 移除并返问队列头部的元素 如果队列为空,则返回null
peek 返回队列头部的元素 如果队列为空,则返回null
put 添加一个元素 如果队列满,则阻塞
take 移除并返回队列头部的元素 如果队列为空,则阻塞
drainTo(list) 一次性取出队列所有元素
用队列实现栈
用栈实现队列
232. 用栈实现队列
用两个栈实现队列,一个作为进栈,一个出栈,调整顺序。
class MyQueue {
Stack<Integer> stackIn = new Stack<Integer>();
Stack<Integer> stackOut = new Stack<Integer>();
public MyQueue() {
stackIn.clear();
stackOut.clear();
}
public void push(int x) {
stackIn.push(x);
}
public int pop() {
int i = this.peek();
return stackOut.pop();
}
public int peek() {
if(stackOut.empty()){
while(!stackIn.empty()){
stackOut.push(stackIn.pop());
}
}
return stackOut.peek();
}
public boolean empty() {
return stackOut.empty() && stackIn.empty();
}
}
225. 用队列实现栈
用两个队列实现栈,其中一个保存栈内的元素,另一个作为辅助队列,调证顺序,使新加入的元素永远位于第一个位置。
优化:一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时在去弹出元素就是栈的顺序了。
class MyStack {
Queue<Integer> deque1 = new LinkedList<Integer>();
Queue<Integer> deque2 = new LinkedList<Integer>();
public MyStack() {
deque1.clear();
deque2.clear();
}
public void push(int x) {
deque2.add(x);
while(!deque1.isEmpty()){
deque2.add(deque1.remove());
}
while (!deque2.isEmpty()){
deque1.add(deque2.remove());
}
}
public int pop() {
return deque1.remove();
}
public int top() {
return deque1.peek();
}
public boolean empty() {
return deque1.isEmpty() && deque2.isEmpty();
}
}
20.有效的括号
1047.删除字符串中的所有相邻重复项**(字符串去重)**
- 逆波兰表达式求值
涉及到之前遍历的元素,以及匹配问题可以考虑栈解决
20.有效的括号
()[]{}
}或者{
(]
分析:用栈解决匹配问题,当前元素如果为左括号栈中添加相应的右括号;如果为右括号,就与栈顶匹配(栈内需不为空),直到最后栈为空就满足条件。
class Solution {
public boolean isValid(String s) {
Stack<Character> st = new Stack<>();
for (int i = 0; i < s.length(); i++){
char c = s.charAt(i);
if(c == '(') st.push(')');
else if(c == '[') st.push(']');
else if(c == '{') st.push('}');
else if(!st.empty() && c == st.peek()){
st.pop();
}else{
return false;
}
}
if(st.empty()) return true;
else return false;
}
}
1047.删除字符串中的所有相邻重复项
方法1: 栈(stack或者直接使用Stringbuffer作为栈)
class Solution {
public String removeDuplicates(String s) {
Stack<Character> st = new Stack<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if(st.empty()){
st.push(c);
}else{
if(st.peek() == c){
st.pop();
}else{
st.push(c);
}
}
}
StringBuffer list = new StringBuffer();
while(!st.empty()){
list.insert(0, st.pop());
}
return new String(list);
}
}
方法3: 由于本题是删除字符,可以用快慢指针
class Solution {
public String removeDuplicates(String s) {
int slow = 0, fast = 0;
char[] chars = s.toCharArray();
while (fast < s.length()){
chars[slow] = chars[fast];
if(slow > 0 && chars[slow] == chars[slow-1]){
slow--;
}else{
slow++;
}
fast++;
}
return String.valueOf(chars, 0, slow);
}
}
注意,减法和除法计算与弹栈的顺序问题
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> st = new Stack<>();
for (String t: tokens){
if(t.equals("+")){
int a = st.pop();
int b = st.pop();
st.push(a+b);
}else if(t.equals("-")){
int a = st.pop();
int b = st.pop();
st.push(b-a); //注意减数与被减数
}else if(t.equals("*")){
int a = st.pop();
int b = st.pop();
st.push(a*b);
}else if(t.equals("/")){
int a = st.pop();
int b = st.pop();
st.push(b/a); //注意除数与被除数
}else{
int i = Integer.parseInt(t);
st.push(i);
}
}
return st.pop();
}
}
单调队列,即单调递减或单调递增的队列。
为了可以同时弹出队首和队尾的元素,一般需要使用双端队列。满足这种单调性的双端队列称作「单调队列」。
队列中的元素在原来的列表中的位置是由前往后的(随着循环顺序入队)。
队列中元素的大小是单调递增或递减的。
从队尾入列,队首或队尾出列。
单调队列一般用于求区间内的最值问题。
- 滑动窗口最大值
239. 滑动窗口最大值
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int [] res = new int[nums.length-k+1];
//que 保存的数组下标
LinkedList<Integer> que = new LinkedList<>();
for (int i = 0; i < nums.length; i++) {
//保证队列单调递减,队首即为当前窗口最大值
while(!que.isEmpty() && nums[i] > nums[que.peekLast()]){
que.removeLast();
}
que.add(i);
//窗口左边界left,右边界i
int left = i - k + 1;
//如果队首下标小于左边界,表示窗口已经划过最大值,必须被删除
if(que.peek() < left){
que.removeFirst();
}
//当窗口内元素大于等于k时,开始保存
if(i + 1 >= k){
res[left] = nums[que.peek()];
}
}
return res;
}
}
官网版:更好理解一点
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int [] res = new int[nums.length-k+1];
//que 保存的数组下标
LinkedList<Integer> que = new LinkedList<>();
//先添加第一个窗口
for (int i = 0; i < k; i++) {
//保证队列单调递减,队首即为当前窗口最大值
while(!que.isEmpty() && nums[que.peekLast()] < nums[i]){
que.removeLast();
}
que.add(i);
}
//第一个最大值
res[0] = nums[que.peek()];
//然后再移动
for (int i = k; i < nums.length; i++) {
//保证队列单调递减,队首即为当前窗口最大值
while(!que.isEmpty() && nums[que.peekLast()] < nums[i]){
que.removeLast();
}
que.add(i);
int left = i - k;
if(que.peek() <= left){
que.remove();
}
res[i-k+1] = nums[que.peek()];
}
return res;
}
}
优先级队列的内部是用堆来维护,而且优先级队列内部元素是自动依照元素的权值排列。
缺省情况下priority_queue利用max-heap(大顶堆)完成对元素的排序,内部以二叉树的形式。
java中用PriorityQueue集合类实现
前 K 个高频元素
数组中的第K个最大元素
使用最小堆统计前k个最大的元素,使用最大堆统计前k个最小的元素
347. 前 K 个高频元素
class Solution {
public int[] topKFrequent(int[] nums, int k) {
//统计元素出现次数
HashMap<Integer, Integer> map = new HashMap<>();
for (int i: nums){
map.put(i, map.getOrDefault(i,0)+1);
}
//构建最小堆,保存元素的值
PriorityQueue<Integer> que = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return map.get(o1) - map.get(o2);
}
});
//当出现次数大于最小值时就弹出最小值,一直维持大小为k的堆,就是前最大k的值
for(Map.Entry<Integer, Integer> entry:map.entrySet()){
if(que.size() < k){
que.add(entry.getKey());
}else if(entry.getValue() > map.get(que.peek())){
que.remove();
que.add(entry.getKey());
}
}
//输出
int [] res = new int[k];
int i = 0;
while(!que.isEmpty()){
res[i++] = que.remove();
}
return res;
}
}
class Solution {
public int findKthLargest(int[] nums, int k) {
//构建最小堆
PriorityQueue<Integer> que = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
//当出现次数大于最小值时就弹出最小值,一直维持大小为k的堆,就是前最大k的值
for(int i: nums){
if(que.size() < k){
que.add(i);
}else if(que.peek() < i){
que.remove();
que.add(i);
}
}
return que.peek();
}
}