问题
1 | Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid. |
翻译:
给定一个只包含字符’(‘,’)’,’{‘,’}’,’[‘和’]’的字符串,判断输入字符串是否有效。
输入字符串在下列情况下有效:
- 开括号必须由相同类型的括号关闭。
- 开括号必须按正确的顺序关闭。
- 注意,空字符串也被认为是有效的。
示例1:
输入:“()”
输出:true
示例2:
输入:“()(){}”
输出:true
示例3:
输入:“()”
输出:false
示例4:
输入:“(())”
输出:false
例5:
输入:“{[]}”
输出:true
解题思路
本题思路很简单,如果左开符号(类似:(,{,[等),那么不管,或者把对应的压入栈,如果遇到右开,则需要判断离他最近的左开符号是否能成对,不能返回失败,如果能则需要去除,避免下一个判断的影响。
这边提供两类思路方案:
方案一:不使用额外空间,只用当前数组,我们遇到右开符号,就要向前进行遍历,遇到第一个不为空的字符,判断是否能成对,如果能则继续,不能返回false。如果最终数组都是空字符串,那么符合要求,返回true,不然返回false。
方案二:使用stack数据结构,把左开符号压入栈中,遇到右开符号,则拿出栈,pop一个,判断是否能成对,如果能则继续,不能返回false,如果最终栈是空的,返回true,否则返回false。
解题方法
第一种解体方法,按照我们的思路来编辑,代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44public boolean isValid(String s) {
char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
char indexItem = chars[i];
char temp = ' ';
switch (indexItem) {
case ']':
temp = '[';
break;
case '}':
temp = '{';
break;
case ')':
temp = '(';
break;
default:
temp = ' ';
}
if (temp != ' ' && !find(chars, i, temp)) {
return false;
}
}
return String.valueOf(chars).trim().length() == 0;
}
private boolean find(char[] chars, int lastIndex, char target) {
for (int i = lastIndex - 1; i >= 0; i--) {
char temp = chars[i];
if (temp == ' ' || temp == ')' || temp == ']' || temp == '}') {
continue;
}
if (chars[i] != target) {
return false;
}
//两者滞空,
chars[i] = ' ';
chars[lastIndex] = ' ';
return true;
}
return false;
}时间复杂度: 该方案用了循环,循环层数为2(算上向前遍历),由于第二层判断计数不好记,设为M,外层循环为n,所以f(n)=(n*M)=Mn;所以O(f(n))=O(Mn),即T(n)=O(n^2)
空间复杂度: 该方案没有使用额外的空间,所以空间复杂度是O(1);
第二种解题方法,按照我们的思路来编辑,代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24Stack<Character> stack = new Stack<>();
int size = s.length();
for (int i = 0; i < size; i++) {
char indexChar = s.charAt(i);
switch (indexChar) {
case '{':
stack.push('}');
break;
case '[':
stack.push(']');
break;
case '(':
stack.push(')');
break;
default:
if (stack.size() == 0) {
return false;
} else if (stack.pop() != indexChar) {
return false;
}
}
}
return stack.size() == 0;时间复杂度: 该方案用了循环,循环层数为1,循环为n,所以f(n)=(n)=n;所以O(f(n))=O(n),即T(n)=O(n)
空间复杂度: 该方案使用栈作为额外,额外的空间最坏的情况就是全部入栈,为n,所以空间复杂度是O(n);
第三种解题方法,按照我们的思路来编辑,代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26LinkedList<Character> linkedList=new LinkedList();
int size = s.length();
for (int i = 0; i < size; i++) {
char indexChar = s.charAt(i);
switch (indexChar) {
case '{':
linkedList.addFirst('}');
break;
case '[':
linkedList.addFirst(']');
break;
case '(':
linkedList.addFirst(')');
break;
default:
if (linkedList.size() == 0) {
return false;
} else if (linkedList.getFirst() != indexChar) {
return false;
}
linkedList.remove(0);
}
}
return linkedList.size() == 0;时间复杂度: 该方案用了循环,循环层数为1,循环为n,所以f(n)=(n)=n;所以O(f(n))=O(n),即T(n)=O(n)
空间复杂度: 该方案使用链表作为额外,额外的空间最坏的情况就是全部入链表,为n,所以空间复杂度是O(n);
总结
本题的大致解法如上所诉,本题两种思路,三种解题方式,第三种主要是选择的数据结构不同,java自带的Stack是继承Vector,是一个线程安全的,但是在本题不需要考虑安全性,所以加锁的开销是多余的,而且不能一开始就设置Stack的大小,在Vector中使用数组作为存储结构,所以当长度足够长,数组扩容时间和空间损耗会比较大,所以选用LinkedList,没有数组扩容的问题,可以随意增加和删除节点。没有必要线程安全,就没有加锁的额外开销了。