栈与队列---算法题
1.括号匹配
题目链接:20. 有效的括号 - 力扣(LeetCode)

算法讲解:
利用栈来解决这个问题
遍历字符串s,如果遇到"("或者"{"或者"[",就完栈中插入该元素,当遇到")"或者"}"或者"]"时,我们就取出栈顶元素,看此时的栈顶元素是否与当前遍历到的右括号是否构成有效的括号组合
此时最终就会有4中情况
第一种情况:
栈是空的并且字符串遍历完成,说明此时字符串是有效括号,返回true即可
第二种情况:括号顺序不匹配
遍历过程中,括号不匹配,此时直接返回false
第三种情况:左括号多
此时就是字符串已经遍历完了,而栈不为空,说明左括号多了,此时也返回false
第四种情况:右括号多
此时就是栈空了,而字符串还没有遍历完成,说明右括号多了。此时也返回false
代码实现:
class Solution {public boolean isValid(String s) {char[] ss=s.toCharArray();Stack<Character> stack = new Stack<>();for(int i=0;i<ss.length;i++){char ch=ss[i];if(ch == '(' || ch == '{' || ch == '['){stack.push(ch);}else{if(stack.isEmpty()) return false;char tmp = stack.pop();if(ch == ')'&&tmp != '(' || ch == '}'&&tmp != '{' || ch == ']'&&tmp != '['){return false;}}}if(!stack.isEmpty()) return false;return true;}
}
2.逆波兰表达式求值
题目链接:150. 逆波兰表达式求值 - 力扣(LeetCode)

题目解析:tokens是一个后缀表达式,返回这个后缀表达式转换为中缀表达式的计算结果
算法讲解:栈
额外补充:前缀表达式如何转换为后缀表达式
前缀表达式从前往后,按照加减乘除的顺序加上对应的括号,然后将对应的符号搬到对应的括号外面即可
遍历字符串tokens,遇到数字就将该数字加到栈中,遇到计算符的话,就取两次栈顶元素,注意:第一次取出的栈顶元素是要作为右操作数,第二次取出的栈顶元素是要作为左操作数,然后再将这两个操作数的计算结果加到栈中即可
最终栈中只会剩下一个元素,这个元素就是后缀表达式转换为中缀表达式,该中缀表达式的结果
代码实现:
class Solution {public int evalRPN(String[] tokens) {Stack<Integer> stack = new Stack<>();for(String s : tokens){if(isNumber(s)){stack.push(Integer.valueOf(s));}else{int val1=stack.pop();int val2=stack.pop();switch(s){case "+":stack.push(val2+val1);break;case "-":stack.push(val2-val1);break;case "*":stack.push(val2*val1);break;case "/":stack.push(val2/val1);break;default:break;}}}return stack.pop();}public boolean isNumber(String s){if(!s.equals("+")&&!s.equals("-")&&!s.equals("*")&&!s.equals("/")){return true;}return false;}
}
3.栈的压入、弹出序列
题目链接:栈的压入、弹出序列_牛客题霸_牛客网

题目解析:pushV是压栈序列,可以一边压栈一边出栈,请判断popV是不是其中一种出栈序列
算法讲解:
遍历pushV,将在pushV遍历到的数字压到栈中,且每次压栈之后,都要判断栈顶元素是否与popV对应的数字相等,如果相等,则出栈,如果不相等,则继续遍历pushV的下一个数字,如果最终popV是一个正确的出栈序列,那么最终栈是一个空栈,如果popV不是一个正确的出栈序列,那么最终栈还会存在元素,且pushV已经遍历完了
代码实现:
public boolean IsPopOrder (int[] pushV, int[] popV) {// write code hereStack<Integer> stack = new Stack();int j=0;for(int i=0;i<pushV.length;i++){int pushv = pushV[i];stack.push(pushv);while(!stack.isEmpty()){int top = stack.peek();int popv = popV[j];if(top == popv){stack.pop();j++;}else{break;}}}if(!stack.isEmpty()) return false;return true;}
4.最长回文子串---字符串
题目链接:5. 最长回文子串 - 力扣(LeetCode)

题目解析:返回字符串s中的最长回文子串
算法讲解:中心扩展算法
遍历字符串s,每次都已遍历到的字符串为中心,定义两个指针left和right,一开始都让他们指向中心,如果s[left]==s[right]且left和right都没越界,此时就让left和right越界或者s[left]!=s[right]
但是以上面的那种方案,只考虑了回文子串的长度为奇数的情况,没有考虑到回文子串长度为偶数的情况,如果考虑偶数的情况下,一开始只要让right在中心的下一个位置或者让left处于中心位置的前一个位置
因为题目要求的是回文子串,无论回文子串长度是偶数还是奇数,最终left指向的位置就是恰好不为回文子串的位置,所以left-1就是为最长回文子串的开始位置
代码实现:
class Solution {public String longestPalindrome(String s) {char[] ss = s.toCharArray();int n=ss.length;int start=0;int maxLength=1;for(int i=0;i<n;i++){char ch=ss[i];int left=i;int right=i;//奇数情况while(left >=0 && right <n && ss[left]==ss[right]){left--;right++;}int tmp = right - left - 1;if(tmp>maxLength){maxLength=tmp;start=left+1;}//偶数情况left = i;right = i + 1;while(left >=0 && right <n && ss[left]==ss[right]){left--;right++;}tmp = right - left - 1;if(tmp>maxLength){maxLength=tmp;start=left+1;} }return s.substring(start,start+maxLength);}
}
算法精品课
5.删除字符串中的所有相邻重复项
题目链接:1047. 删除字符串中的所有相邻重复项 - 力扣(LeetCode)

题目解析:删除字符串s中的相邻字符串
算法讲解:利用栈来模拟
先创建一个栈,然后去遍历字符串,每次遍历到一个字符,假设此时遍历到的字符为ch,此时就去栈中看,如果栈中没有元素的话,此时可以直接将ch插入到栈中。但是如果栈中有元素的话,此时就要拿ch和栈顶元素比较,如果ch和栈顶元素相同,此时直接删除栈顶元素即可,就不用往栈中插入元素了,最终,栈中存储的字符就是最终想要的结果
但是此时没必要真的创建一个栈,如果使用栈的话,最终在返回结果时,还要对结果逆序才是正确的结果
此时可以利用一个字符串来模拟栈
代码实现:
class Solution {public String removeDuplicates(String s) {StringBuffer ret = new StringBuffer();//字符串模拟栈char[] ss = s.toCharArray();for(char ch : ss){if(ret.length()>0 && ch==ret.charAt(ret.length()-1)){ret.deleteCharAt(ret.length()-1);}else{ret.append(ch);}}return ret.toString();}
}
6.比较含退格的字符串
题目链接:844. 比较含退格的字符串 - 力扣(LeetCode)

题目解析:#字符代表退格,判断两个字符串进行退格后,两个字符串是否相同
算法讲解:利用栈来模拟
这道题和上一道题的解题思路一模一样,此时还是不用真正得创建一个栈,还是用一个字符串老模拟栈
如何获取字符串进行退格后的样子呢?
先遍历字符串,如果遇到#字符,此时直接出栈即可(出栈时栈不能为空),如果不是#字符,此时直接入栈即可
代码实现:
class Solution {public boolean backspaceCompare(String s, String t) {String s1 = check(s);String t1 = check(t);return s1.equals(t1);}public String check(String str){StringBuffer ret = new StringBuffer();char[] ss=str.toCharArray();for(char ch:ss){if(ch=='#'){if(ret.length()>0) ret.deleteCharAt(ret.length()-1);}else{ret.append(ch);}}return ret.toString();}
}
7.基本计算器 II
题目链接:227. 基本计算器 II - 力扣(LeetCode)

算法讲解:栈+一个符号字符op
首先创建一个栈和一个符号字符op,将op初始化为‘+’,将op初始化为 + zifu是为了最终在统计结果时统一都是加法运算
此时遍历字符串,会遇到三种情况:
第一种情况:遇到空格时,此时直接跳过
第二种情况:遇到数字
此时遇到数字的话,可能这个数字是个多位数,所以要通过一个循环将这个数字提取出来
将数字提取出来之后,假设提取出来的数字是tmp,根据op的情况进行处理
如果op是 + ,此时直接将tmp入栈
如果op是 - ,此时直接将tmp的相反数入栈
如果op是 * ,此时获取栈顶元素(poll),假设栈顶元素为top,然后将top*tmp的结果入栈
如果op是 / ,此时获取栈顶元素(poll),假设栈顶元素为top,然后将top / tmp的结果入栈
第三种情况就是遇到符号字符,此时更新op即可
将栈中存储的数据全部相加,就是题目想要的结果
代码实现:
class Solution {public int calculate(String _s) {Stack<Integer> st = new Stack<>();char[] s = _s.toCharArray();int len = s.length;int i=0;char op='+';while(i<len){if(s[i]==' '){i++;}else if(Character.isDigit(s[i])){//提取数字tmpint tmp=0;while(i<len&&Character.isDigit(s[i])){tmp=tmp*10+(s[i]-'0');i++;}if(op=='+') st.push(tmp);else if(op=='-') st.push(-tmp);else if(op=='*') st.push(st.pop()*tmp);else st.push(st.pop()/tmp);}else {op=s[i];i++;}}int ret=0;while(!st.isEmpty()){ret+=st.pop();}return ret;}
}
经典面试题
1.设计循环队列
题目链接:622. 设计循环队列 - 力扣(LeetCode)

题目解析:设计一个循环队列
算法讲解:
这道题的主要思路就是如何判断队列是否为满,队列是否为空
首先如何判断队列是否为空呢?
此时创建两个变量,front和rear,当front==rear时,说明此时队列为空

如何判断循环队列是满的呢?
此时我的做法是浪费一个空间,当(rear+1)%arr.length=front时,就代表循环队列满了,注意这里不能使用rear+1==front来判断队列为满

如何插入元素呢?
此时先判断队列是否为空,如果队列不为空,此时让arr[rear]=val,然后在让rear往后走,rear往后走的公式为:rear=(rear+1)%arr.length
如何删除元素呢?
此时先判断队列是否为空,如果队列不为空,此时直接让front往后走即可,即让front=(front+1)%arr.length
如何从对首获取元素呢?返回arr[front]即可
如何获取从对尾获取元素呢?
此时我们可能会直接想到返回arr[rear-1],但是如果此时rear==0的话,此时让rear-1就会越界了,如下图
此时只需要针对rear==0这种情况进行特殊处理即可,当rear==0的时候,就要返回arr[arr.length-1]

代码实现:
class MyCircularQueue {int[] arr;int front;int rear;public MyCircularQueue(int k) {/**注意这里是k+1,因为我们浪费了一个空间来判满,但是此时要保证队列要插入k个元素所以要创建一个k+1大小的数组*/arr=new int[k+1];}public boolean enQueue(int value) {if(isFull()){return false;}arr[rear]=value;rear=(rear+1)%arr.length;return true;}public boolean deQueue() {if(isEmpty()){return false;}front=(front+1)%arr.length;return true;}public int Front() {if(isEmpty()){return -1;}return arr[front]; }public int Rear() {if(isEmpty()){return -1;}int index = rear==0?arr.length-1:rear-1;return arr[index]; }public boolean isEmpty() {return rear==front;}public boolean isFull() {return (rear+1)%arr.length==front;}
}
2.用队列实现栈
题目链接:225. 用队列实现栈 - 力扣(LeetCode)

题目解析:用队列实现栈
算法讲解:用2个队列来实现栈
模拟出栈操作,此时出栈的花是要将34取出来,但是队列只能取出对首元素,此时只能从qu2中取出12,此时可以将12和23都取出放到另外一个队列qu1中,然后qu2剩下的元素就是栈顶元素了,总结:出栈操作就是将不为空的队列的size-1个元素插入到另一个队列中


入栈操作:我们只需往不为空的队列中插入元素即可。
模拟取栈顶元素:此时通过一个中间变量,将不为空的队列中的数据一个一个的poll出来,创建一个中间变量val来记录每一次poll的值,最终val记录的就是栈顶元素

代码实现:
class MyStack {Queue<Integer> q1;Queue<Integer> q2;public MyStack() {q1=new LinkedList<>();q2=new LinkedList<>();}public void push(int x) {if(empty()){q1.offer(x);return;}if(!q1.isEmpty()){q1.offer(x);}else{q2.offer(x);}} public int pop() {if(empty()){return -1;}if(!q1.isEmpty()){while(q1.size()>1){q2.offer(q1.poll());}return q1.poll();}else{while(q2.size()>1){q1.offer(q2.poll());}return q2.poll();}}public int top() {if(empty()){return -1;}int val=0;if(!q1.isEmpty()){while(q1.size()>0){val=q1.poll();q2.offer(val);}return val;}else{while(q2.size()>0){val=q2.poll();q1.offer(val);}return val;} }public boolean empty() {return q1.isEmpty()&&q2.isEmpty();}
}
3.用栈实现队列
题目链接:232. 用栈实现队列 - 力扣(LeetCode)

题目解析:用栈实现队列
算法讲解:用两个栈来实现队列
模拟入栈:模拟入队列时,我们专门往第一个栈插入元素即可
模拟出栈:此时分两种情况,此时如果第二个栈为空,此时就需要将第一个栈中的元素全部迁移到第二个栈中,然后直接st2.pop即可,如下图

还有一种情况,如果st2不为空,此时直接取出栈顶元素即可
代码实现:
class MyQueue {Stack<Integer> st1;Stack<Integer> st2;public MyQueue() {st1=new Stack<>();st2=new Stack<>();}public void push(int x) {st1.push(x);}public int pop() {if(empty()){return -1;}if(!st2.isEmpty()){return st2.pop();}else{while(!st1.isEmpty()){st2.push(st1.pop());}return st2.pop();}}public int peek() {if(empty()){return -1;}if(!st2.isEmpty()){return st2.peek();}else{while(!st1.isEmpty()){st2.push(st1.pop());}return st2.peek();} }public boolean empty() {return st1.isEmpty()&&st2.isEmpty();}
}
