基本递归,检查平衡括号

时间:2010-04-26 03:41:21

标签: algorithm recursion stack

我过去编写的软件使用堆栈来检查平衡方程式,但现在我被要求递归地编写一个类似的算法来检查正确的嵌套括号和括号。

  

好例子:()[]()   ([]()[])

     

不好的例子:((]([)]

假设我的函数被调用:isBalanced。

每次传递是否应评估较小的子字符串(直到达到2的基本情况)?或者,我应该总是评估完整的字符串并向内移动索引吗?

12 个答案:

答案 0 :(得分:51)

首先,对于您的原始问题,请注意,如果您使用非常长的字符串,则每次进行函数调用时,您不希望制作精确的副本减去一个字母。因此,您应该支持使用索引或验证您选择的语言是否在幕后制作副本。

其次,我遇到了使用堆栈数据结构的所有答案的问题。我认为你的任务的重点是让你明白,通过递归你的函数调用创建一个堆栈。您不需要使用堆栈数据结构来保存括号,因为每个递归调用都是隐式堆栈上的新条目。

我将演示一个匹配()的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!

答案 1 :(得分:44)

有很多方法可以做到这一点,但最简单的算法是简单地从左向右处理,将堆栈作为参数传递

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实现。请注意,为了方便起见,我现在已经切换它,以便堆栈推入前面而不是字符串的后面。我也修改了它,以便它只是跳过非括号符号而不是将其报告为错误。

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

答案 2 :(得分:3)

这个想法是保留一个打开的括号列表,如果你找到一个关闭的括号,检查它是否关闭了最后一个打开:

  • 如果这些括号匹配,则从opensBrackets列表中删除最后一个打开并继续以字符串的其余部分递归检查
  • 否则你会发现一个支架可以关闭一个打开的神经,因此它不平衡。

当字符串最后为空时,如果括号列表也为空(所有括号都已关闭),请返回true,否则false

ALGORITHM (在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);
    }
}

<强> TEST

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;

答案 3 :(得分:2)

 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;
    }
}

答案 4 :(得分:1)

从逻辑的角度来看并不重要 - 如果你保留一堆当前不平衡的parens,你传递给递归的每一步,你永远不需要向后看,所以它如果你在每次递归调用中剪切字符串,或者只是增加一个索引并且只查看当前的第一个字符,这无关紧要。

在大多数具有非可变字符串的编程语言中,缩短字符串可能比在字符串上传递稍大的字符串更昂贵(性能方面)。另一方面,在像C这样的语言中,你可以只增加char数组中的指针。我猜这很依赖于语言,这两种方法中的哪一种更“有效”。从概念的角度来看,它们都是等价的。

答案 5 :(得分:0)

我想说这取决于你的设计。您可以使用两个计数器或使用两个不同的符号进行堆叠,也可以使用递归来处理它,区别在于设计方法。

答案 6 :(得分:0)

在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中进行转换。

答案 7 :(得分:0)

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
}

答案 8 :(得分:0)

用于检查平衡括号的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);
?>

答案 9 :(得分:0)

平衡括号(JS)

更直观的解决方案是像这样使用堆栈:

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所述,您还可以在递归函数中使用堆栈,而无需向后看

答案 10 :(得分:-1)

应该是堆栈的简单使用..

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;       

    }

答案 11 :(得分:-1)

@ 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