一句话总结:栈的思想不难,难的是实现细节。
原题链接:
原理很简单,遇到左括号入栈,遇到右括号就跟栈顶比较。如果组成了一对正确的括号则继续比较,否则返回false,直到遍历完整个字符串。写法细节上比较有说法:首先,用哈希表来存储每一对括号(题解写法是用右括号作为key,左括号作为value,实际上换一下也是可以的),可以很轻松的利用哈希表的get()方法来比较是否可以组成一对正确的括号;其次,事先计算字符串的长度是否为偶数可以剪枝;最后只需返回全部遍历完成以后栈是否为空即可。
class Solution {
public boolean isValid(String s) {
int n = s.length();
if (n % 2 == 1) return false;
Map<Character, Character> pair = new HashMap<>();
pair.put('}', '{');
pair.put(']', '[');
pair.put(')', '(');
Deque<Character> stack = new LinkedList<>();
for (int i = 0; i < n; i++) {
char c = s.charAt(i);
if (pair.containsKey(c)) {
if (stack.isEmpty() || stack.peek() != pair.get(c)) return false;
stack.pop();
} else stack.push(c);
}
return stack.isEmpty();
}
}
然后是执行更快速的写法:用一个与字符串等长的字符数组来作为栈,另外再设置一个指针p,遍历字符串的时候遇到左括号就将其放在数组中,指针向后移一位;遇到右括号则与指针的前一位比较是否配对,不是则返回false,是则将指针向前移动一位,到前面一个未配对左括号的后一位上。直到遍历完整个字符串,最后指针如果为0表示所有括号均可配对,正确。
class Solution {
public boolean isValid(String s) {
int p = 0, n = s.length();
char[] opStack = new char[n];
for (int i = 0; i < n; i++) {
char c = s.charAt(i);
if (c == '(' || c == '[' || c == '{') {
opStack[p++] = c;
} else if (c == ')') {
if (p > 0 && opStack[p - 1] == '(') p--;
else return false;
} else if (c == ']') {
if (p > 0 && opStack[p - 1] == '[') p--;
else return false;
} else if (c == '}') {
if (p > 0 && opStack[p - 1] == '{') p--;
else return false;
}
}
return p == 0;
}
}
原题链接:1047 删除字符串中的所有相邻重复项
此题原理也不难。遍历字符串,如果此时遍历到的字符与前一个相等,就将其前一个字符删除;否则将该字符添加到字符串尾。难的是StringBuffer的操作。
class Solution {
public String removeDuplicates(String s) {
StringBuffer sb = new StringBuffer();
for (char ch : s.toCharArray()) {
if (sb.length() != 0 && sb.charAt(sb.length() - 1) == ch) {
sb.deleteCharAt(sb.length() - 1);
} else sb.append(ch);
}
return sb.toString();
}
}
另外还有一个利用快慢指针的解法,有点巧妙。先将字符串转换为字符数组,然后快指针每轮遍历往后移动一位,慢指针的操作非常关键,它在每轮遍历中先将其所指元素赋值为快指针所指元素,然后比较其是否与前一位字符相等,是则慢指针指向前一位,否则也向后移动一位。最后返回从0到慢指针所指位置的子串即可。
class Solution {
public String removeDuplicates(String s) {
char[] cs = s.toCharArray();
int fast = 0, slow = 0;
while (fast < cs.length) {
cs[slow] = cs[fast];
if (slow > 0 && cs[slow] == cs[slow - 1]) --slow;
else ++slow;
++fast;
}
return new String(cs).substring(0, slow);
}
}
原题链接:150 逆波兰表达式
此题思想不难,需要考虑的是equals方法的使用细节(是用"+".equals(token)而非token.equals("+")),以及最后针对除法的写法细节,需要用两个临时变量将栈顶两个元素依次出栈保存,再压入计算结果。另一个比较不容易想到的是Integer.valueOf()方法,这个方法可以将字符串转换为int类型的数字。
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new LinkedList<>();
for (String token : tokens) {
if ("+".equals(token)) stack.push(stack.pop() + stack.pop());
else if ("-".equals(token)) stack.push(-stack.pop() + stack.pop());
else if ("*".equals(token)) stack.push(stack.pop() * stack.pop());
else if ("/".equals(token)) {
int num1 = stack.pop(), num2 = stack.pop();
stack.push(num2 / num1);
} else stack.push(Integer.valueOf(token));
}
return stack.pop();
}
}