文章目录
20. 有效的括号
问题描述
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
解决(新建一个栈,入栈出栈,啥算法都没有)
class Solution {
public boolean isValid(String s) {
if (s.length() % 2 == 1) {
return false;
}
char[] charArray=s.toCharArray();
Stack<Character> stack = new Stack<Character>();
for (int i=0;i<charArray.length;i++){
if('('==charArray[i] || '['==charArray[i] || '{'==charArray[i])
stack.push(charArray[i]);
else if(')'==charArray[i]){
if(stack.size() > 0 && '(' == stack.peek())
stack.pop();
else
return false;
}
else if(']'==charArray[i]){
if(stack.size() > 0 && '[' == stack.peek())
stack.pop();
else
return false;
}
else if('}'==charArray[i]){
if(stack.size() > 0 && '{' == stack.peek())
stack.pop();
else
return false;
}
}
if (stack.size()==0)
return true;
return false;
}
}
1047. 删除字符串中的所有相邻重复项
问题描述
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
输入:“abbaca”
输出:“ca”
解释:
例如,在 “abbaca” 中,我们可以删除 “bb” 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 “aaca”,其中又只有 “aa” 可以执行重复项删除操作,所以最后的字符串为 “ca”。
提示:
1 <= S.length <= 20000
S 仅由小写英文字母组成。
解题关键:无论是先删除哪一个,都不会影响最终的结果
充分理解题意后,我们可以发现,当字符串中同时有多组相邻重复项时,我们无论是先删除哪一个,都不会影响最终的结果。因此我们可以从左向右顺次处理该字符串。
而消除一对相邻重复项可能会导致新的相邻重复项出现,如从字符串abba 中删除 bb 会导致出现新的相邻重复项 \text{aa}aa 出现。因此我们需要保存当前还未被删除的字符。一种显而易见的数据结构呼之欲出:栈。我们只需要遍历该字符串,如果当前字符和栈顶字符相同,我们就贪心地将其消去,否则就将其入栈即可。
解决
class Solution {
public String removeDuplicates(String S) {
char[] charArray = S.toCharArray();
Stack<Character> stack=new Stack<Character>();
for (int i=0;i<charArray.length;i++){
if (stack.size()==0){
stack.push(charArray[i]);
}else{
if (charArray[i] == stack.peek()){
stack.pop();
}else {
stack.push(charArray[i]);
}
}
}
Stack outStack=new Stack();
while (stack.size()>0){
outStack.push(stack.pop());
}
StringBuffer stringBuffer=new StringBuffer();
while(outStack.size()>0){
stringBuffer.append(outStack.pop());
}
return stringBuffer.toString();
}
}
栈满足,尾部插入,获取尾部,删除尾部,
但是不满足从头部拿出,所以用了两个栈+一个StringBuffer
用到了两个栈作为一个队列,还不如只用用队列(双端队列),LinkedList
class Solution {
public String removeDuplicates(String S) {
char[] charArray = S.toCharArray();
Deque<Character> deque=new LinkedList<>(); // 要从队列尾巴删除元素,只能用Deque,用Queue无法得到最后一个元素
for (int i=0;i<charArray.length;i++){
if (deque.size()==0){
deque.offer(charArray[i]); //队列末尾添加元素
}else{
if (charArray[i] == deque.getLast()){ // 获取队列最尾巴一个元素
deque.removeLast(); // 队列删除最后一个元素
}else {
deque.offer(charArray[i]);
}
}
}
StringBuffer stringBuffer=new StringBuffer();
while(deque.size()>0){
stringBuffer.append(deque.pop()); // 从队列头部,将元素一个个拿出来
}
return stringBuffer.toString();
}
}
双向队列满足,尾部插入,获取尾部,删除尾部,
满足从头部拿出,所以用了一个双向队列+一个StringBuffer
Queue 中 add() 和 offer()都是用来向队列添加一个元素。offer是安全的
在容量已满的情况下,add() 方法会抛出IllegalStateException异常,offer() 方法只会返回 false 。
Queue 中 remove() 和 poll()都是用来从队列头部删除一个元素。poll是安全的
在队列元素为空的情况下,remove() 方法会抛出NoSuchElementException异常,poll() 方法只会返回 null 。
LinkedList还要转为StringBuffer输出,还不如直接用StringBuffer/StringBuilder
class Solution {
public String removeDuplicates(String S) {
char[] charArray = S.toCharArray();
StringBuffer stringbuffer=new StringBuffer(); // 要从队列尾巴删除元素,只能用Deque,用Queue无法得到最后一个元素
for (int i=0;i<charArray.length;i++) {
if (stringbuffer.length() == 0) {
stringbuffer.append(charArray[i]); //队列末尾添加元素
} else {
if (charArray[i] == stringbuffer.charAt(stringbuffer.length()-1)) { // 获取队列最尾巴一个元素
stringbuffer.deleteCharAt(stringbuffer.length()-1); // 队列删除最后一个元素
} else {
stringbuffer.append(charArray[i]); // 尾部插入
}
}
}
return stringbuffer.toString(); // 直接输出
}
}
换成stringbuilder也是一样的,这个题目没必要考虑线程安全
class Solution {
public String removeDuplicates(String S) {
char[] charArray = S.toCharArray();
StringBuilder stringbuilder=new StringBuilder(); // 要从队列尾巴删除元素,只能用Deque,用Queue无法得到最后一个元素
for (int i=0;i<charArray.length;i++) {
if (stringbuilder.length() == 0) {
stringbuilder.append(charArray[i]); //队列末尾添加元素
} else {
if (charArray[i] == stringbuilder.charAt(stringbuilder.length()-1)) { // 获取队列最尾巴一个元素
stringbuilder.deleteCharAt(stringbuilder.length()-1); // 队列删除最后一个元素
} else {
stringbuilder.append(charArray[i]); // 尾部插入
}
}
}
return stringbuilder.toString(); // 直接输出
}
}
String拼接字符串底层还是走StringBuilder,效率太低
小结:栈和队列是LinkedList是阉割版,Deque是完整的LinkedList,自己用字符串/字符数组也可以。
Stack/Queue < LinkedList/Deque < StringBuffer/StringBuilder/charArray
150. 逆波兰表达式(后缀表达式)求值
解决(栈中只存放数字,运算符是出栈信号,所以直接将Stack的泛型设置为Integer)
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack=new Stack<Integer>();
for (int i=0;i<tokens.length;i++){
String token=tokens[i];
if (isNumber(token)){
stack.push(Integer.parseInt(token));
}else{
Integer token1= stack.pop();
Integer token2= stack.pop();
if ("+".equals(token)){
stack.push(token2 + token1);
}
else if ("-".equals(token)){
stack.push(token2 - token1);
}
else if ("*".equals(token)){
stack.push(token2 * token1);
}
else if ("/".equals(token)){
stack.push(token2 / token1);
}
}
}
return stack.pop();
}
private boolean isNumber(String token){
return !("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token));
}
}
能用栈就能用LinkedList,就能用StringBuffer
class Solution {
public int evalRPN(String[] tokens) {
List<Integer> stack=new LinkedList<>(); // 双向队列
for (int i=0;i<tokens.length;i++){
String token=tokens[i];
if (isNumber(token)){
stack.add(Integer.parseInt(token)); // 尾部插入,这里的Integer.parseInt不能少,因为tokens是字符串数组,stack是整型栈
}else{
Integer token1= stack.remove(stack.size()-1); // 尾部抛出
Integer token2= stack.remove(stack.size()-1);
if ("+".equals(token)){
stack.add(token2 + token1); // 尾部插入
}
else if ("-".equals(token)){
stack.add(token2 - token1); // 尾部插入
}
else if ("*".equals(token)){
stack.add(token2 * token1); // 尾部插入
}
else if ("/".equals(token)){
stack.add(token2 / token1); // 尾部插入
}
}
}
return stack.get(0);
}
// 数字是罗列不完 0~100 -100~0 无数
private boolean isNumber(String token){
return !("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token));
}
}
这里不能用StringBuffer了(最后是输出结果,不是输出字符串),只能用Integer数组,存放数字,用数组不好,因为插入和删除元素,而且数组无法确定初始容量