Basic Recursion, Check Balanced Parenthesis

前端 未结 12 1144
孤独总比滥情好
孤独总比滥情好 2020-11-29 17:56

I\'ve written software in the past that uses a stack to check for balanced equations, but now I\'m asked to write a similar algorithm recursively to check for properly neste

相关标签:
12条回答
  • 2020-11-29 18:08

    I would say this depends on your design. You could either use two counters or stack with two different symbols or you can handle it using recursion, the difference is in design approach.

    0 讨论(0)
  • 2020-11-29 18:09

    There are many ways to do this, but the simplest algorithm is to simply process forward left to right, passing the stack as a parameter

    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"
    

    Here it is implemented in Java. Note that I've switched it now so that the stack pushes in front instead of at the back of the string, for convenience. I've also modified it so that it just skips non-parenthesis symbols instead of reporting it as an error.

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

    Test harness:

        String[] tests = {
            "()[]<>{}",
            "(<",
            "]}",
            "()<",
            "(][)",
            "{(X)[XY]}",
        };
        for (String s : tests) {
            System.out.println(s + " = " + isBalanced(s, ""));
        }
    

    Output:

    ()[]<>{} = true
    (< = false
    ]} = false
    ()< = false
    (][) = false
    {(X)[XY]} = true
    
    0 讨论(0)
  • 2020-11-29 18:09

    Balanced Parenthesis (JS)

    The more intuitive solution is to use stack like so:

    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

    And using recursive function (without using stack), might look something like so:

    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

    * As @Adrian mention, you can also use stack in the recursive function without the need of looking backwards

    0 讨论(0)
  • 2020-11-29 18:12

    It doesn't really matter from a logical point of view -- if you keep a stack of all currently un-balanced parens that you pass to each step of the recursion, you'll never need to look backwards, so it doesn't matter if you cut up the string on each recursive call, or just increment an index and only look at the current first character.

    In most programming languages, which have non-mutable strings, it's probably more expensive (performance-wise) to shorten the string than it is to pass a slightly larger string on the stack. On the other hand, in a language like C, you could just increment a pointer within the char array. I guess it's pretty language-dependent which of these two approaches is more 'efficient'. They're both equivalent from a conceptual point of view.

    0 讨论(0)
  • 2020-11-29 18:12

    In the Scala programming language, I would do it like this:

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

    Please edit to make it perfect.

    I was only suggesting a conversion in Scala.

    0 讨论(0)
  • 2020-11-29 18:14

    The idea is to keep a list of the opened brackets, and if you find a closing brackt, check if it closes the last opened:

    • If those brackets match, then remove the last opened from the list of openedBrackets and continue to check recursively on the rest of the string
    • Else you have found a brackets that close a nerver opened once, so it is not balanced.

    When the string is finally empty, if the list of brackes is empty too (so all the brackes has been closed) return true, else false

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

    OUTPUT:

    abcdefksdhgs  ->  true
    [{aaa<bb>dd}]<232>  ->  true
    [ff{<gg}]<ttt>  ->  false
    {<}>  ->  false
    

    Note that i have imported the following classes:

    import java.util.HashMap;
    import java.util.LinkedList;
    import java.util.Map;
    
    0 讨论(0)
提交回复
热议问题