【发布时间】:2011-02-12 06:04:31
【问题描述】:
我过去编写过使用堆栈来检查平衡方程的软件,但现在我被要求编写一个类似的递归算法来检查正确嵌套的括号和括号。
很好的例子:() [] () ([]()[])
不好的例子:( (] ([)]
假设我的函数被调用:isBalanced。
是否应该每次通过评估一个较小的子字符串(直到达到 2 左的基本情况)?或者,我是否应该始终评估整个字符串并将索引向内移动?
【问题讨论】:
我过去编写过使用堆栈来检查平衡方程的软件,但现在我被要求编写一个类似的递归算法来检查正确嵌套的括号和括号。
很好的例子:() [] () ([]()[])
不好的例子:( (] ([)]
假设我的函数被调用:isBalanced。
是否应该每次通过评估一个较小的子字符串(直到达到 2 左的基本情况)?或者,我是否应该始终评估整个字符串并将索引向内移动?
【问题讨论】:
更直观的解决方案是像这样使用堆栈:
function isBalanced(str) {
const parentesis = {
'(': ')',
'[': ']',
'{': '}',
};
const closing = Object.values(parentesis);
const stack = [];
for (let char of str) {
if (parentesis[char]) {
stack.push(parentesis[char]);
} else if (closing.includes(char) && char !== stack.pop()) {
return false;
}
}
return !stack.length;
}
console.log(isBalanced('{[()]}')); // true
console.log(isBalanced('{[(]]}')); // false
console.log(isBalanced('([()]')); // false
使用递归函数(不使用堆栈),可能看起来像这样:
function isBalanced(str) {
const parenthesis = {
'(': ')',
'[': ']',
'{': '}',
};
if (!str.length) {
return true;
}
for (let i = 0; i < str.length; i++) {
const char = str[i];
if (parenthesis[char]) {
for (let j = str.length - 1; j >= i; j--) {
const _char = str[j];
if (parenthesis[_char]) {
return false;
} else if (_char === parenthesis[char]) {
return isBalanced(str.substring(i + 1, j));
}
}
} else if (Object.values(parenthesis).includes(char)) {
return false;
}
}
return true;
}
console.log(isBalanced('{[()]}')); // true
console.log(isBalanced('{[(]]}')); // false
console.log(isBalanced('([()]')); // false
* 正如@Adrian 提到的,您也可以在递归函数中使用堆栈,而无需向后看
【讨论】:
在 Scala 编程语言中,我会这样做:
def balance(chars: List[Char]): Boolean = {
def process(chars: List[Char], myStack: Stack[Char]): Boolean =
if (chars.isEmpty) myStack.isEmpty
else {
chars.head match {
case '(' => process(chars.tail, myStack.push(chars.head))
case ')' => if (myStack.contains('(')) process(chars.tail, myStack.pop)
else false
case '[' => process(chars.tail, myStack.push(chars.head))
case ']' => {
if (myStack.contains('[')) process(chars.tail, myStack.pop) else false
}
case _ => process(chars.tail, myStack)
}
}
val balancingAuxStack = new Stack[Char]
process(chars, balancingAuxStack)
}
请进行编辑以使其完美。
我只是建议在 Scala 中进行转换。
【讨论】:
检查平衡括号的PHP解决方案
<?php
/**
* @param string $inputString
*/
function isBalanced($inputString)
{
if (0 == strlen($inputString)) {
echo 'String length should be greater than 0';
exit;
}
$stack = array();
for ($i = 0; $i < strlen($inputString); $i++) {
$char = $inputString[$i];
if ($char === '(' || $char === '{' || $char === '[') {
array_push($stack, $char);
}
if ($char === ')' || $char === '}' || $char === ']') {
$matchablePairBraces = array_pop($stack);
$isMatchingPair = isMatchingPair($char, $matchablePairBraces);
if (!$isMatchingPair) {
echo "$inputString is NOT Balanced." . PHP_EOL;
exit;
}
}
}
echo "$inputString is Balanced." . PHP_EOL;
}
/**
* @param string $char1
* @param string $char2
* @return bool
*/
function isMatchingPair($char1, $char2)
{
if ($char1 === ')' && $char2 === '(') {
return true;
}
if ($char1 === '}' && $char2 === '{') {
return true;
}
if ($char1 === ']' && $char2 === '[') {
return true;
}
return false;
}
$inputString = '{ Swatantra (() {} ()) Kumar }';
isBalanced($inputString);
?>
【讨论】:
func evalExpression(inStringArray:[String])-> Bool{
var status = false
var inStringArray = inStringArray
if inStringArray.count == 0 {
return true
}
// determine the complimentary bracket.
var complimentaryChar = ""
if (inStringArray.first == "(" || inStringArray.first == "[" || inStringArray.first == "{"){
switch inStringArray.first! {
case "(":
complimentaryChar = ")"
break
case "[":
complimentaryChar = "]"
break
case "{":
complimentaryChar = "}"
break
default:
break
}
}else{
return false
}
// find the complimentary character index in the input array.
var index = 0
var subArray = [String]()
for i in 0..<inStringArray.count{
if inStringArray[i] == complimentaryChar {
index = i
}
}
// if no complimetary bracket is found,so return false.
if index == 0{
return false
}
// create a new sub array for evaluating the brackets.
for i in 0...index{
subArray.append(inStringArray[i])
}
subArray.removeFirst()
subArray.removeLast()
if evalExpression(inStringArray: subArray){
// if part of the expression evaluates to true continue with the rest.
for _ in 0...index{
inStringArray.removeFirst()
}
status = evalExpression(inStringArray: inStringArray)
}
return status
}
【讨论】:
@indiv 的回答很好,足以解决括号语法问题。如果您想使用堆栈或不想使用递归方法,您可以查看 github 上的 python script。它简单快捷。
BRACKET_ROUND_OPEN = '('
BRACKET_ROUND__CLOSE = ')'
BRACKET_CURLY_OPEN = '{'
BRACKET_CURLY_CLOSE = '}'
BRACKET_SQUARE_OPEN = '['
BRACKET_SQUARE_CLOSE = ']'
TUPLE_OPEN_CLOSE = [(BRACKET_ROUND_OPEN,BRACKET_ROUND__CLOSE),
(BRACKET_CURLY_OPEN,BRACKET_CURLY_CLOSE),
(BRACKET_SQUARE_OPEN,BRACKET_SQUARE_CLOSE)]
BRACKET_LIST = [BRACKET_ROUND_OPEN,BRACKET_ROUND__CLOSE,BRACKET_CURLY_OPEN,BRACKET_CURLY_CLOSE,BRACKET_SQUARE_OPEN,BRACKET_SQUARE_CLOSE]
def balanced_parentheses(expression):
stack = list()
left = expression[0]
for exp in expression:
if exp not in BRACKET_LIST:
continue
skip = False
for bracket_couple in TUPLE_OPEN_CLOSE:
if exp != bracket_couple[0] and exp != bracket_couple[1]:
continue
if left == bracket_couple[0] and exp == bracket_couple[1]:
if len(stack) == 0:
return False
stack.pop()
skip = True
left = ''
if len(stack) > 0:
left = stack[len(stack) - 1]
if not skip:
left = exp
stack.append(exp)
return len(stack) == 0
if __name__ == '__main__':
print(balanced_parentheses('(()())({})[]'))#True
print(balanced_parentheses('((balanced)(parentheses))({})[]'))#True
print(balanced_parentheses('(()())())'))#False
【讨论】:
public static boolean isBalanced(String str) {
if (str.length() == 0) {
return true;
}
if (str.contains("()")) {
return isBalanced(str.replaceFirst("\\(\\)", ""));
}
if (str.contains("[]")) {
return isBalanced(str.replaceFirst("\\[\\]", ""));
}
if (str.contains("{}")) {
return isBalanced(str.replaceFirst("\\{\\}", ""));
} else {
return false;
}
}
【讨论】:
这个想法是保留一个打开的括号列表,如果你找到一个右括号,检查它是否关闭了最后一个打开的:
当字符串最终为空时,如果括号列表也为空(因此所有括号都已关闭)返回true,否则返回false
算法(Java):
public static boolean isBalanced(final String str1, final LinkedList<Character> openedBrackets, final Map<Character, Character> closeToOpen) {
if ((str1 == null) || str1.isEmpty()) {
return openedBrackets.isEmpty();
} else if (closeToOpen.containsValue(str1.charAt(0))) {
openedBrackets.add(str1.charAt(0));
return isBalanced(str1.substring(1), openedBrackets, closeToOpen);
} else if (closeToOpen.containsKey(str1.charAt(0))) {
if (openedBrackets.getLast() == closeToOpen.get(str1.charAt(0))) {
openedBrackets.removeLast();
return isBalanced(str1.substring(1), openedBrackets, closeToOpen);
} else {
return false;
}
} else {
return isBalanced(str1.substring(1), openedBrackets, closeToOpen);
}
}
测试:
public static void main(final String[] args) {
final Map<Character, Character> closeToOpen = new HashMap<Character, Character>();
closeToOpen.put('}', '{');
closeToOpen.put(']', '[');
closeToOpen.put(')', '(');
closeToOpen.put('>', '<');
final String[] testSet = new String[] { "abcdefksdhgs", "[{aaa<bb>dd}]<232>", "[ff{<gg}]<ttt>", "{<}>" };
for (final String test : testSet) {
System.out.println(test + " -> " + isBalanced(test, new LinkedList<Character>(), closeToOpen));
}
}
输出:
abcdefksdhgs -> true
[{aaa<bb>dd}]<232> -> true
[ff{<gg}]<ttt> -> false
{<}> -> false
请注意,我已导入以下类:
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
【讨论】:
首先,对于您最初的问题,请注意,如果您使用的是非常长的字符串,您不希望每次进行函数调用时都制作精确的副本减去一个字母。因此,您应该倾向于使用索引或验证您选择的语言不是在幕后复制。
其次,我对这里所有使用堆栈数据结构的答案都有疑问。我认为您分配的重点是让您了解通过递归您的函数调用创建堆栈。您不需要使用堆栈数据结构来保存括号,因为每个递归调用都是隐式堆栈上的新条目。
我将演示一个匹配( 和) 的C 程序。添加[ 和] 等其他类型是读者的练习。我在函数中维护的只是我在字符串中的位置(作为指针传递),因为递归是我的堆栈。
/* Search a string for matching parentheses. If the parentheses match, returns a
* pointer that addresses the nul terminator at the end of the string. If they
* don't match, the pointer addresses the first character that doesn't match.
*/
const char *match(const char *str)
{
if( *str == '\0' || *str == ')' ) { return str; }
if( *str == '(' )
{
const char *closer = match(++str);
if( *closer == ')' )
{
return match(++closer);
}
return str - 1;
}
return match(++str);
}
用这段代码测试过:
const char *test[] = {
"()", "(", ")", "", "(()))", "(((())))", "()()(()())",
"(() ( hi))) (())()(((( ))))", "abcd"
};
for( index = 0; index < sizeof(test) / sizeof(test[0]); ++index ) {
const char *result = match(test[index]);
printf("%s:\t", test[index]);
*result == '\0' ? printf("Good!\n") :
printf("Bad @ char %d\n", result - test[index] + 1);
}
输出:
(): Good!
(: Bad @ char 1
): Bad @ char 1
: Good!
(())): Bad @ char 5
(((()))): Good!
()()(()()): Good!
(() ( hi))) (())()(((( )))): Bad @ char 11
abcd: Good!
【讨论】:
())( 上运行测试程序时,我得到“Bad @ char 3”,这对我来说看起来不错。 ideone.com/e.js/VBM8IU
is_balanced 委托给这个函数。整个“返回指向第一个不匹配的指针”实际上是调用者不必理解的实现细节。
应该是栈的简单使用..
private string tokens = "{([<})]>";
Stack<char> stack = new Stack<char>();
public bool IsExpressionVaild(string exp)
{
int mid = (tokens.Length / 2) ;
for (int i = 0; i < exp.Length; i++)
{
int index = tokens.IndexOf(exp[i]);
if (-1 == index) { continue; }
if(index<mid ) stack .Push(exp[i]);
else
{
if (stack.Pop() != tokens[index - mid]) { return false; }
}
}
return true;
}
【讨论】:
有很多方法可以做到这一点,但最简单的算法是简单地从左到右处理,将堆栈作为参数传递
FUNCTION isBalanced(String input, String stack) : boolean
IF isEmpty(input)
RETURN isEmpty(stack)
ELSE IF isOpen(firstChar(input))
RETURN isBalanced(allButFirst(input), stack + firstChar(input))
ELSE IF isClose(firstChar(input))
RETURN NOT isEmpty(stack) AND isMatching(firstChar(input), lastChar(stack))
AND isBalanced(allButFirst(input), allButLast(stack))
ELSE
ERROR "Invalid character"
这里是用Java实现的。请注意,为了方便起见,我现在已将其切换为将堆栈推入 front 而不是 back 字符串。我还对其进行了修改,使其只是跳过非括号符号而不是将其报告为错误。
static String open = "([<{";
static String close = ")]>}";
static boolean isOpen(char ch) {
return open.indexOf(ch) != -1;
}
static boolean isClose(char ch) {
return close.indexOf(ch) != -1;
}
static boolean isMatching(char chOpen, char chClose) {
return open.indexOf(chOpen) == close.indexOf(chClose);
}
static boolean isBalanced(String input, String stack) {
return
input.isEmpty() ?
stack.isEmpty()
: isOpen(input.charAt(0)) ?
isBalanced(input.substring(1), input.charAt(0) + stack)
: isClose(input.charAt(0)) ?
!stack.isEmpty() && isMatching(stack.charAt(0), input.charAt(0))
&& isBalanced(input.substring(1), stack.substring(1))
: isBalanced(input.substring(1), stack);
}
测试工具:
String[] tests = {
"()[]<>{}",
"(<",
"]}",
"()<",
"(][)",
"{(X)[XY]}",
};
for (String s : tests) {
System.out.println(s + " = " + isBalanced(s, ""));
}
输出:
()[]<>{} = true
(< = false
]} = false
()< = false
(][) = false
{(X)[XY]} = true
【讨论】:
从逻辑的角度来看,这并不重要——如果你保留一个堆栈,所有当前不平衡的括号传递给递归的每一步,你就永远不需要向后看,所以它如果您在每次递归调用时剪切字符串,或者只是增加一个索引并只查看当前的第一个字符,这并不重要。
在大多数具有非可变字符串的编程语言中,缩短字符串可能比在堆栈上传递稍大的字符串更昂贵(在性能方面)。另一方面,在像 C 这样的语言中,您可以在 char 数组中增加一个指针。我想这两种方法中哪一种更“有效”取决于语言。从概念的角度来看,它们都是等价的。
【讨论】:
我会说这取决于您的设计。您可以使用两个计数器或堆栈两个不同的符号,或者您可以使用递归处理它,区别在于设计方法。
【讨论】: