LeetCode 20 有效的括号
题目链接:20. 有效的括号
本题是栈的经典应用——消消乐。
【解题思路】
仔细分析题意,可以发现本题括号不匹配的场景其实只有三个:左括号多余、括号类型不匹配、右括号多余。
解题突出一个消消乐,我们创建一个栈,当匹配到左括号时,将其对应类型的右括号入栈,当匹配到右括号时,与栈顶元素进行匹配,此时会出现三种情况:
1.遍历完字符串,但是栈不为空;说明左括号没有对应的右括号进行匹配,因此返回false。
2.栈顶元素与当前遍历到的字符不匹配;说明字符串中的括号类型不匹配,因此返回false。
3.还没遍历完,栈就已经空了;说明右括号没有相应的左括号进行匹配,因此返回false。
【代码部分】
java:
class Solution {
public boolean isValid(String s) {
Deque<Character> deque = new LinkedList<>();
char ch;
for(int i = 0; i < s.length(); i++){
ch = s.charAt(i);
if(ch == '('){
deque.push(')');
}else if(ch == '{'){
deque.push('}');
}else if(ch == '['){
deque.push(']');
}else if(deque.isEmpty() || deque.peek() != ch){
return false;
}else{
deque.pop();
}
}
return deque.isEmpty();
}
}
【疑难点】
1.一定要先判断栈是否为空再判断栈内括号是否匹配!否则可能会在空栈里操作,产生错误!
2.要先判断左括号的各种类型是否被遍历到再判断栈是否为空!否则可能会出现类似“(){}[]”这样的情况导致判断出错!
LeetCode 1047 删除字符串中所有相邻重复项
题目链接:1047. 删除字符串中的所有相邻重复项
本题还是消消乐。
【解题思路】
遍历整个字符串,遇见字符后与栈顶匹配:
1.栈为空——直接将字符入栈。
2.栈顶字符相同——说明为相同重复字符,将栈内字符出栈。
3.栈顶字符不同——说明不为相同重复字符,将字符入栈。
【解题步骤】
本题既可以用栈辅助寻找相邻重复项,也可以直接在字符串上操作,然而java中的String并不支持修改,因此无论怎样,都不能在原字符上操作。
解法一
1.创建一个栈用来匹配与记录遍历的字符;
2.创建一个字符类变量ch用来接收每次遍历到的字符;
3.判断栈是否为空(栈顶元素是否与ch相等):
栈为空——直接将字符入栈;
栈不为空且栈顶字符相同——说明为相同重复字符,将栈顶字符弹出;
栈不为空且栈顶字符不同——说明不为相同重复字符,将字符入栈。
4.创建一个字符串类型变量str来接收栈内字符;
5.循环将栈内字符传入字符串str中,直到栈为空;
6.返回字符串str。
解法二
1.创建一个StringBuilder类型变量res用来修改字符串;
2.用一个int类型变量top当指针,指向栈顶,初始化为-1。
3.循环遍历的操作与解法1 相似,但是出栈和入栈有所不同:
出栈操作为:StringBuilder.deleteCharAt(top),执行完毕后top需要减一;
入栈操作为:StringBuilder.append(ch),执行完毕后top需要加一。
【代码部分】
java解法一:
class Solution {
public String removeDuplicates(String S) {
ArrayDeque<Character> deque = new ArrayDeque<>();
char ch;
for (int i = 0; i < S.length(); i++) {
ch = S.charAt(i);
if (deque.isEmpty() || deque.peek() != ch) {
deque.push(ch);
} else {
deque.pop();
}
}
String str = "";
while (!deque.isEmpty()) {
str = deque.pop() + str;
}
return str;
}
}
java解法二:
class Solution {
public String removeDuplicates(String s) {
//用StringBuffer来修改字符串,速度没有用StringBuilder来得快
//StringBuffer res = new StringBuffer();
StringBuilder res = new StringBuilder();
int top = -1;//用top当做栈顶的指针,指向当前字符串的首字符,栈空时为-1,因此初始化为-1
for(int i = 0; i < s.length(); i++){
char ch = s.charAt(i);
if(top >= 0 && res.charAt(i) == ch){
res.deleteCharAt(top);
top --;
}else{
res.append(ch);
top++;
}
}
return res.toString();
}
}
【疑难点】
1.为什么解法1中栈的初始化要用ArrayDeque而不用LinkList?
答:ArrayDeque会比LinkedList在除了删除元素这一点外会快一点。
2.为什么解法2中要用StringBuilder而不用StringBuffer?
答:用 StringBuilder 来修改字符串,速度更快。
3.为什么解法2中指向栈顶的指针TOP需要初始化为-1?
答:用top当做栈顶的指针,指向当前字符串的首字符,栈空时为-1,因此初始化为-1。
LeetCode 150 逆波兰表达式求值
题目链接:150. 逆波兰表达式求值
本题的解题思路广义上来看其实和前两题一样,本质上也是消消乐,对于这类“判断两相邻字符操作”的题,用栈往往能轻松解决。
【解题思路】
首先我们得理解逆波兰表达式是什么;
逆波兰表达式是一种后缀表达式,所谓后缀就是指运算符写所有数字后面的表达式。
采用逆波兰表达式的优点有:
1.相较于常见的中序表达式,逆波兰表达式去掉括号后表达式无歧义。
2.逆波兰表达式适合用栈运算,遇到数字则入栈;遇到运算符则取出栈顶两个数字进行运算,将运算之后的结果压入栈中。
【解题步骤】
1.新建一个LinkdeList类型的栈用来记录我们遍历到的元素
2.遍历表达式,判断每个遍历到的元素:
如果等于数字,则入栈;
如果等于加减乘除,则取出栈顶两个元素进行运算,得出结果再入栈。
3.将栈顶元素返回,即为结果。
【代码部分】
java:
class Solution {
public int evalRPN(String[] tokens) {
Deque <Integer> stack = new LinkedList<>();
for(String s : tokens){
if("+".equals(s)){
stack.push(stack.pop() + stack.pop());
}else if("-".equals(s)){
stack.push(-stack.pop() + stack.pop());
}else if("*".equals(s)){
stack.push(stack.pop() * stack.pop());
}else if("/".equals(s)){
int temp1 = stack.pop();
int temp2 = stack.pop();
stack.push(temp2 / temp1);
}else{
stack.push(Integer.valueOf(s));
}
}
return stack.pop();
}
}
【疑难点】
1.注意在入栈时,记得将元素转换为整形,再进行入栈!
2.注意!“-”和“/”需要特殊处理、——需要将弹出栈的两个元素位置顺序交换一下再进行运算。