这篇就到栈和队列啦~~~加油加油!奥利给!
思路分析:
卧槽这个我一遍就过了,感觉只有这么简单了,只要明白栈和队列的特性就可以了。具体看代码注释就没什么问题了。
代码如下:
class MyQueue {
//我们需要先声明两个栈
Stack<Integer> stack1;
Stack<Integer> stack2;
//在构造函数中对两个栈进行初始化
public MyQueue() {
stack1 = new Stack<>();
stack2 = new Stack<>();
}
public void push(int x) {
stack1.push(x); //往stack1里面压栈
}
public int pop() {
//因为栈先进后出,队列是先进先出
//所以我们将stack1中的数据全部又放入stack2中
while(stack1.size() > 0){
stack2.push(stack1.pop());
}
//现在将stack2中pop出去第一个即最先进入队列中的值
int res = stack2.pop();
//然后再把这些值又放回stack1中
while(stack2.size() > 0){
stack1.push(stack2.pop());
}
//返回该值
return res;
}
public int peek() {
//因为栈先进后出,队列是先进先出
//所以我们将stack1中的数据全部又放入stack2中
while(stack1.size() > 0){
stack2.push(stack1.pop());
}
//将stack2中pop出去第一个,用res记录下来
int res = stack2.pop();
//因为peek方法只是查看
//所以我们还需要将这个值再放回stack2中
stack2.push(res);
//然后再把这些数据再放回stack1中
while(stack2.size() > 0){
stack1.push(stack2.pop());
}
//返回该值
return res;
}
public boolean empty() {
//这个就很简单啦
//因为我们存数据的栈只是stack1
//所以我们只要判断stack1的大小如果为0则队列就为空
//否则不为空
if(stack1.size() == 0) return true;
else return false;
}
}
/**
* 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();
*/
题目描述:
思路分析:
水题,就和之前的栈模拟队列一样,只要明白栈是先进后出而队列是先进先出的特性就好做,代码中注释非常清楚。
代码如下:
class MyStack {
//我们先需要声明两个队列
Queue<Integer> queue1;
Queue<Integer> queue2;
public MyStack() {
//在构造器中实例化着两个队列
//Queue为抽象类无法实例化
//所以Java中队列一般都用LinkedList来实现
queue1 = new LinkedList<>();
queue2 = new LinkedList<>();
}
public void push(int x) {
//用queue1来存储数据
queue1.add(x);
}
public int pop() {
//因为栈先进后出,队列先进先出
//所以我们先清空queue1除了最后一个数据的其他所有数据
//这些被清空的数据暂时放入queue2中
while(queue1.size() > 1){
queue2.add(queue1.remove());
}
//用res保存queue1的独苗
int res = queue1.remove();
//然后再将之前的数据放回queue1中
while(queue2.size() > 0){
queue1.add(queue2.remove());
}
//返回该值
return res;
}
public int top() {
//因为栈先进后出,队列先进先出
//所以我们先清空queue1除了最后一个数据的其他所以数据
//这些被清空的数据先放入queue2中
while(queue1.size() > 1){
queue2.add(queue1.remove());
}
//用res先保存下queue1的独苗
int res = queue1.remove();
//取到值后因为top只是查看队首元素
//所以我们再刚刚移除的元素再放回queue2中凑完整
queue2.add(res);
//然后再将一整个这些数据放回queue1中
while(queue2.size() > 0){
queue1.add(queue2.remove());
}
//返回该值
return res;
}
public boolean empty() {
//因为只有queue1用来存数据
//所以如果queue1长度为0的话,则队列为空返回true
//否则返回false
if(queue1.size() == 0) return true;
else return false;
}
}
/**
* 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();
*/
代码如下:
class Solution {
public boolean isValid(String s) {
//因为栈的特性,所以对称匹配类的题目用栈很好做
//但还是有一定的技巧性,这个要多做多练就能get的
//创建一个栈
Stack<Character> stack = new Stack<>();
//遍历字符串s
for(int i=0; i<s.length(); i++){
//如果当前字符为左括号,那么我们往栈中加入对应的右括号
//如果当前字符为右括号,则会对栈是否为空或者栈顶字符是否与其相同进行判断
//如果栈为空,说明没有与之对应的左括号,出现不够匹配的情况返回false
//或者栈顶字符与当前字符不相同,这时肯定会出现匹配矛盾的情况,return false
//最后如果是栈顶字符和当前字符相同的话,匹配上了,则删除该栈顶字符即可
if(s.charAt(i) == '(') stack.push(')');
else if(s.charAt(i) == '{') stack.push('}');
else if(s.charAt(i) == '[') stack.push(']');
// 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号 return false
// 第二种情况:遍历字符串匹配的过程中,发现栈里没有我们要匹配的字符。所以return false
else if(stack.isEmpty() || stack.peek() != s.charAt(i)) return false;
else stack.pop();
}
//第一种情况:此时我们已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false,否则就return true
return stack.isEmpty();
}
}
思路分析:
其实就用了个栈,没有多难,看代码注释就能看明白。
代码如下:
class Solution {
public String removeDuplicates(String s) {
Stack<Character> stack = new Stack<>();
for(int i=0; i<s.length(); i++){
//当前元素和栈顶元素相同,所以应该删除
if(stack.size()>0 && s.charAt(i) == stack.peek()){
stack.pop();
}else{//如果不相同,那么加入栈中
stack.push(s.charAt(i));
}
}
//最后将栈中所有的数据转化为字符串即可
StringBuilder sb = new StringBuilder();
while(stack.size()>0){
sb.append(stack.pop());
}
//因为栈是先进后出,所以还需要反转一下该字符串再进行返回
String str = sb.toString();
StringBuilder sbb = new StringBuilder(str).reverse();
return sbb.toString();
}
}
题目描述:
思路分析:
也不是很难,就是只要明白什么叫逆波兰表达式就很好做,代码注释中写的很明白了。
代码如下:
class Solution {
public int evalRPN(String[] tokens) {
//题目不难,关键是要指导啥是逆波兰表达式
//逆波兰表达式:是一种后缀表达式
//所谓后缀就是把运算符写在后面
//波兰表达式同样是栈的典型应用
//先创建一个栈
Stack<Integer> stack = new Stack<>();
//循环判断
for(int i=0; i<tokens.length ;i++){
//如果遇到的是运算符,那么就要弹出对应的两个数字进行运算
if(tokens[i].equals("+") || tokens[i].equals("-") || tokens[i].equals("*") || tokens[i].equals("/")){
//取到弹出栈的两个值
int a = stack.pop();
int b = stack.pop();
//下面分情况进行讨论
if(tokens[i].equals("+")) {
stack.push(a + b);
}
//因为栈是先进后出,所以减数和被减数需要换个位置进行减法操作
if(tokens[i].equals("-")) {
stack.push(b - a);
}
if(tokens[i].equals("*")) {
stack.push(a * b);
}
//除法也要替换,原因同上
if(tokens[i].equals("/")) {
stack.push(b / a);
}
}else{
//如果当前字符串为数字的话,就存入栈中
stack.push(Integer.valueOf(tokens[i]));
}
}
//最后栈肯定会存在唯一一个值,就是我们的运算结果
//返回即可
return stack.pop();
}
}
题目描述:
题目分析:
其实我也不是很明白…暴力是可以解的(在牛客上不会超时),但是在LeetCode上会超时,看了很多题解说是用单调队列来做,我看的不是很明白(倒懂不懂的)…等后面思维提升了复盘的时候再来重新分析吧。
代码如下:
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums.length == 1) {
return nums;
}
int len = nums.length - k + 1;
//存放结果元素的数组
int[] res = new int[len];
int num = 0;
//自定义队列
MyQueue myQueue = new MyQueue();
//先将前k的元素放入队列
for (int i = 0; i < k; i++) {
myQueue.add(nums[i]);
}
res[num++] = myQueue.peek();
for (int i = k; i < nums.length; i++) {
//滑动窗口移除最前面的元素,移除是判断该元素是否放入队列
myQueue.poll(nums[i - k]);
//滑动窗口加入最后面的元素
myQueue.add(nums[i]);
//记录对应的最大值
res[num++] = myQueue.peek();
}
return res;
}
}
//自定义单调队列
class MyQueue {
//刷题多了刷到一个技巧
//用比如LinkedList或者ArrayList时
//最好前面类型也用LinkedList或者ArrayList
//别用List接口或者抽象类之类的
LinkedList<Integer> deque = new LinkedList<>();
//弹出元素时,比较当前要弹出的数值是否等于队列出口的数值,如果相等则弹出
//同时判断队列当前是否为空
void poll(int val) {
if (!deque.isEmpty() && val == deque.peek()) {
deque.remove();
}
}
//添加元素时,如果要添加的元素大于入口处的元素,就将入口元素弹出
//保证队列元素单调递减
//比如此时队列元素3,1,2将要入队,比1大,所以1弹出,此时队列:3,2
void add(int val) {
while (!deque.isEmpty() && val > deque.getLast()) {
deque.removeLast();
}
deque.add(val);
}
//队列队顶元素始终为最大值
int peek() {
return deque.peek();
}
}
思路分析:
遇到这种有一一对应关系的题目,肯定都会想到HashMap来做,但我做这个时其实忘记了一个API,就是HashMap如何提取一对一对的KV键值对,用entrySet即可,得记下来,如下:
for(Map.Entry<Integer,Integer> entry : map.entrySet()){
if(entry.getValue() > maxTimes){
maxTimes = entry.getValue();
}
}
还可以使用entry.getKey()拿到Key键。
然后这种题还有个小技巧,在做题时可以留意一下,注释中会有提到。
代码如下:
class Solution {
public int[] topKFrequent(int[] nums, int k) {
//这个感觉可以用哈希表做
//map的key键存元素值,value值存出现次数(即频率)
HashMap<Integer,Integer> map = new HashMap<>();
//创建结果数组
int[] res = new int[k];
//遍历计数即可
for(int i=0; i<nums.length; i++){
if(map.containsKey(nums[i])){
map.put(nums[i],map.get(nums[i])+1);
}else{
map.put(nums[i],1);
}
}
//接下来找到value值最大的前k个key键组成数组返回即可
int maxTimes = 0; //计数器,记录出现频率最高的元素的出现次数
//找出出现次数最多的元素出现的次数
for(Map.Entry<Integer,Integer> entry : map.entrySet()){
if(entry.getValue() > maxTimes){
maxTimes = entry.getValue();
}
}
//按出现次数从大到小往结果数组中添加元素
while(k>0){
for(Map.Entry<Integer,Integer> entry : map.entrySet()){
if(entry.getValue() == maxTimes){
k--;
res[k] = entry.getKey();
}
}
//每回都将maxTimes自减1,逐个比较
//这样一定能找到出现频率最高的前k个元素而不会遗漏
//这个小技巧可以记忆一下
maxTimes--;
}
return res;
}
}