需求:
给定一个字符串所表示的括号序列,包含以下字符: '(', ')'
, '{'
, '}'
,'['
and']'
, 判定是否是有效的括号序列。
分析:
1、对于有效的括号序列,满足下列特点:
1)有效的括号序列长度是不为0的偶数,所以字符串长度为0或者奇数时就返回false
2)如果第一个字符是右括号或者最后一个字符是左括号,那么不是有效括号序列,会返回false
上述1)和2)可以直接判断不是有效括号序列,这样就减少了内存以及时间开销。比如对于"{(){"序列,如果不进行2)的判断,可能需要开辟堆栈空间,判断到最后才得出false的结论,但是如果一开始就判断开头和结尾是否满足有效括号序列的条件,就可以不开辟堆栈空间,进而减少内存开销,提高代码效率。
2、数据结构选用堆栈
堆栈是一种LIFO(后进先出)的数据结构。遍历字符串,将左括号推入堆栈,对于右括号,判断栈顶元素与其是否匹配,如果不匹配就不有效,否则弹出栈顶元素,说明该层的括号已经判断完毕,继续遍历字符串进行判断。遍历之后,需要判断堆栈是否为空,如果不是空的,说明左括号多于右括号,不有效,返回false,否则返回true。
3、java中的堆栈实现
1) Stack类
JDK1.0提供了Stack类,即堆栈类,有如下方法
boolean empty()//判断堆栈是否为空
E push(E e)//将元素e推入栈顶
E peek()//获取栈顶元素
E pop()//弹出栈顶元素
int search(Object obj)//此方法返回距堆栈顶部最近的出现位置到堆栈顶部的距离;堆栈中最顶部项的距离为 1。使用equals 方法比较o 与堆栈中的项。
2)Deque接口
Deque(Double ended queue双端队列)接口实现了提供LIFO堆栈操作的一系列更完整和更一致的set,应该优先使用Deque的子类,而非Stack,比如:
Deque<Integer> deq = new ArrayDeque<Integer>();
在将双端队列用作队列时,将得到 FIFO(先进先出)行为。将元素添加到双端队列的末尾,从双端队列的开头移除元素。从Queue接口继承的方法完全等效于Deque方法;双端队列也可用作 LIFO(后进先出)堆栈。应优先使用此接口而不是遗留Stack
类。在将双端队列用作堆栈时,元素被推入双端队列的开头并从双端队列开头弹出。堆栈方法完全等效于Deque方法。
ArrayDeque是Deque的子类,既可以实现堆栈,又可以实现队列。ArrayDeque用作堆栈时,效率比Stack高,用作队列时,效率比LinkedList高,所以一般使用ArrayDeque来实现堆栈和队列的数据结构。
3)Deque接口方法摘要
a)判断
boolean isEmpty()//判断是否是空的
b)添加元素
void push(E e)//将元素e推入表示的堆栈栈顶
boolean offer(E e)//在双端队列尾部添加元素等价于offerLast()
boolean offerFirst(E e)//在双端队列头部添加元素
boolean offerLast(E e)//在双端队列尾部添加元素
使用offer替换add方法
c)获取元素
E peek()//获取双端队列头部元素,等价于peekFirst()
E peekFirst()//获取双端队列头部元素
E peekLast()//获取双端队列尾部元素
使用peek替换get方法
d)删除元素
E pop()//弹出栈顶元素
E poll()//获取并移除队列头部元素,等价于pollFirst()
E pollFirst()
E pollLast()//获取并移除双端队列尾部元素
使用poll代替remove
e)获取元素个数
int size()
4)ArrayDeque
用作堆栈:push(E e) pop() peek()/peekFirst()
用作队列:offer(E e)/offerLast(E e) poll() peek()
步骤:
1、判断分析中的1)和2)条件,提高代码效率。如果返回false,程序结束,否则进行步骤2
2、创建堆栈,遍历字符串,如果是左括号,则推入堆栈中,如果是右括号,判断堆栈是否是空的,如果是空的返回false,否则看栈顶元素是否和该右括号匹配,如果不匹配返回fasle,如果匹配就将栈顶弹出,继续遍历字符串
3、字符串遍历结束,判断堆栈是否是空的,如果不是空的,说明左括号数量大于右括号,不是有效括号序列,返回false,否则返回true
代码:
public class Solution {
/*
* @param s: A string
* @return: whether the string is a valid parentheses
*/
public boolean isValidParentheses(String s) {
// write your code here
//如果字符串为null或者长度是奇数或者0,那么就不是有效括号,就返回false
//如果字符串的头部是右括号或者尾部是左括号,返回fasle
int len = s.length();
if(s == null || len == 0 || len % 2 != 0)
{
return false;
}
char start = s.charAt(0);
char end = s.charAt(len - 1);
if(start == '}' || start == ')' || start == ']')
{
return false;
}
if(end == '{' || end == '(' || end == '[')
{
return false;
}
//创建堆栈
Deque<Character> stack = new ArrayDeque<Character>();
//遍历字符串,如果是左括号就添加到堆栈中,如果是右括号,看是否和栈顶元素匹配,匹配就pop栈顶元素,不匹配就返回false,如果遍历到最后一个元素,并且和栈顶元素匹配即可返回true,否则fasle
for(int i = 0; i < len; i++)
{
char ch = s.charAt(i);
//如果是左括号就直接添加进堆栈
if(ch == '{' || ch == '(' || ch == '[')
{
stack.push(ch);
}
//如果是右括号,进行匹配判断
else if(ch == '}' || ch ==')' || ch ==']')
{
if(stack.isEmpty())
{
return false;
}
else
{
char peek = stack.peek();
if(isMatch(peek, ch))
{
stack.pop();
}
else
{
return false;
}
}
}
}
//判断堆栈是否是空的,如果是空的,就是
if(stack.isEmpty())
{
return true;
}
return false;
}
//判断是否是匹配的括号对
public boolean isMatch(char c1, char c2)
{
return (c1 == '{' && c2 == '}' || c1 == '(' && c2 == ')' || c1 == '[' && c2 == ']')? true: false;
}
}