Java栈
Stack stack = new Stack<>();
栈是vector的子类,而vector可理解为容纳不同元素类型的可变长数组。
常用的方法:empty() peek() push() pop()以及search()。
Java队列
LinkedList类实现了Queue接口,因此我们可以把LinkedList当成Queue来用。
Queue queue = new LinkedList();
常用的方法:size() offer() poll() 以及 peek()/element()
leetcoe 232 用栈实现队列
注意栈的常用方法的使用。创建两个栈,其中一个当作工具栈。
class MyQueue {
public Stack<Integer> stack1;
public Stack<Integer> stack2;
public MyQueue() {
this.stack1 = new Stack<Integer>();
this.stack2 = new Stack<Integer>();
}
public void push(int x) {
stack1.push(x);
}
public int pop() {
while(!stack1.empty()){
int temp = stack1.pop();
stack2.push(temp);
}
int res = stack2.pop();
while(!stack2.empty()){
stack1.push(stack2.pop());
}
return res;
}
public int peek() {
while(!stack1.empty()){
int temp = stack1.pop();
stack2.push(temp);
}
int res = stack2.peek();
while(!stack2.empty()){
stack1.push(stack2.pop());
}
return res;
}
public boolean empty() {
return stack1.empty();
}
}
/**
* 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();
*/
leetcode 225 用队列实现栈
class MyStack {
Queue<Integer> queue1;
Queue<Integer> queue2;
public MyStack() {
this.queue1 = new LinkedList<Integer>();
this.queue2 = new LinkedList<Integer>();
}
public void push(int x) {
queue1.offer(x);
}
public int pop() {
int size = queue1.size();
while(size > 1){
queue2.offer(queue1.poll());
size--;
}
size = queue2.size();
while(size > 0){
queue1.offer(queue2.poll());
size--;
}
return queue1.poll();
}
public int top() {
int size = queue1.size();
while(size > 1){
queue2.offer(queue1.poll());
size--;
}
int res = queue1.poll();
size = queue2.size();
while(size > 0){
queue1.offer(queue2.poll());
size--;
}
queue1.offer(res);
return res;
}
public boolean empty() {
return (queue1.size() == 0);
}
}
/**
* 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();
*/
leetcode 20 有效的括号
应用场景:Linux cd命令进入目录,编译器词法分析。
注意Stack stack <>里要为引用类型。总共有三种情况输出false:1.左边的括号多余;2.右边的括号多余 3.中间的括号不匹配
class Solution {
public boolean isValid(String s) {
char[] chars = s.toCharArray();
Stack<String> stack = new Stack<String>();
int i = 0;
for(i = 0; i < chars.length; i++){
if(chars[i] == '('){
stack.push(")");
continue;
}
if(chars[i] == '['){
stack.push("]");
continue;
}
if(chars[i] == '{'){
stack.push("}");
continue;
}
if(chars[i] == ')' && !stack.empty()){
String temp = stack.pop();
if(temp == ")"){
continue;
}else{
return false;
}
}
if(chars[i] == ']' && !stack.empty()){
String temp = stack.pop();
if(temp == "]"){
continue;
}else{
return false;
}
}
if(chars[i] == '}' && !stack.empty()){
String temp = stack.pop();
if(temp == "}"){
continue;
}else{
return false;
}
}
if(chars[i] != ' ' && stack.empty()){
return false;
}
}
if(i == chars.length && !stack.empty()){
return false;
}else{
return true;
}
}
}
leetcode 1047 删除字符串中的所有相邻重复项
利用栈,注意字符串的比较为string1.equals(string2)。将字符转换成字符串的方法有:String.valueOf(char)以及Character.toString(char)。
class Solution {
public String removeDuplicates(String s) {
char[] chars = s.toCharArray();
Stack<String> stack = new Stack<String>();
Stack<String> stack2 = new Stack<String>();
String string = "";
for(int i = 0; i < chars.length; i++){
if(!stack.empty() && (Character.toString(chars[i])).equals(stack.peek())){
stack.pop();
continue;
}
stack.push(Character.toString(chars[i]));
}
while(!stack.empty()){
stack2.push(stack.pop());
}
while(!stack2.empty()){
string += stack2.pop();
}
return string;
}
}
逆波兰表达式/后缀表达式
平常人们习惯的算数表达式又可称为中序表达式,其实就是二叉树的中序遍历。如(1 + 2)*(3 + 4)。而逆波兰表达式就是二叉树的后序遍历(左右根),即:1 2 + 3 4 + *。逆波兰表达式是方便计算机顺序计算便可得出结果,而不需要加上括号改变优先级。
使用栈来计算逆波兰表达式:遇见数字则入栈,遇见运算符则计算后再将计算结果入栈,以此往复。其最终结果也就是栈里的最后一个留下的元素。
栈擅长处理相邻字符的消除操作。
注意:字符串转换成数字:Integer.valueOf(str)以及Double.valueOf(str)。
注意:出栈的两个元素进行运算之前要先交换顺序。
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<Integer>();
int left;
int right;
int res;
for(int i = 0; i < tokens.length; i++){
if(!tokens[i].equals("+") && !tokens[i].equals("-") && !tokens[i].equals("*") && !tokens[i].equals("/")){
stack.push(Integer.valueOf(tokens[i]));
}else{
left = stack.pop();
right = stack.pop();
if(tokens[i].equals("+")){
res= right + left;
stack.push(res);
continue;
}
if(tokens[i].equals("-")){
res= right - left;
stack.push(res);
continue;
}
if(tokens[i].equals("*")){
res= right * left;
stack.push(res);
continue;
}
if(tokens[i].equals("/")){
res= right / left;
stack.push(res);
continue;
}
}
}
return stack.pop();
}
}
求滑动窗口的最大值
注意:不能使用优先队列(大顶堆/小顶堆),因为poll()出来的元素不一定是由于滑动窗口的移动而落到滑动窗口外面的元素。
方法:使用单调队列(若后加入的元素的值大于队列前面的元素,则比其小的元素全部出队)。维护队列出口处的元素为滑动窗口的最大值
class Solution {
public void pop(Deque<Integer> deque, int val){
if(!deque.isEmpty() && val == deque.peekFirst()){
deque.pollFirst();
}
}
public void push(Deque<Integer> deque, int val){
while(!deque.isEmpty() && val > deque.peekLast()){
deque.pollLast();
}
deque.offerLast(val);
}
public int getMax(Deque<Integer> deque){
return deque.peekFirst();
}
public int[] maxSlidingWindow(int[] nums, int k) {
int[] res = new int[nums.length - k + 1];
int count = 0;
//定义双端队列
Deque<Integer> deque = new ArrayDeque<Integer>();
deque.offerLast(nums[0]);
for(int i = 1; i < k; i++){
while(!deque.isEmpty() && nums[i] > deque.peekLast()){
deque.pollLast();
}
deque.offerLast(nums[i]);
}
res[0] = getMax(deque);
while(count < nums.length - k){
pop(deque,nums[count]);
push(deque,nums[count + k]);
res[++count] = getMax(deque);
}
return res;
}
}
leetcode 347 前k个高频元素
利用优先队列(大顶堆或小顶堆)。本题使用小顶堆。
class Solution {
public int[] topKFrequent(int[] nums, int k) {
HashMap<Integer,Integer> map = new HashMap<>();
//创建小顶堆
PriorityQueue<Integer> pq = new PriorityQueue<>();
int[] res = new int[k];
int j = 0;
for(int i = 0; i < nums.length; i++){
if(map.containsKey(nums[i])){
int temp = map.get(nums[i]);
map.put(nums[i], temp + 1);
}else{
map.put(nums[i], 1);
}
}
map.forEach((key, value)->{
pq.offer(value);
if(pq.size() > k){
pq.poll();
}
});
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if (pq.size() != 0 && entry.getValue().equals(pq.poll())) {
res[j] = entry.getKey();
j++;
}
if(pq.size() == 0){
break;
}
}
return res;
}
}
正确代码:
// 优先级队列,为了避免复杂 api 操作,pq 存储数组
// lambda 表达式设置优先级队列从大到小存储 o1 - o2 为从大到小,o2 - o1 反之
PriorityQueue<int[]> pq = new PriorityQueue<>((o1, o2) -> o1[1] - o2[1]);
int[] res = new int[k]; // 答案数组为 k 个元素
Map<Integer, Integer> map = new HashMap<>(); // 记录元素出现次数
for(int num : nums) map.put(num, map.getOrDefault(num, 0) + 1);
for(var x : map.entrySet()) { // entrySet 获取 k-v Set 集合
// 将 kv 转化成数组
int[] tmp = new int[2];
tmp[0] = x.getKey();
tmp[1] = x.getValue();
pq.offer(tmp);
if(pq.size() > k) {
pq.poll();
}
}
for(int i = 0; i < k; i ++) {
res[i] = pq.poll()[0]; // 获取优先队列里的元素
}
return res;