【问题标题】:Checking string has balanced parentheses检查字符串是否有平衡括号
【发布时间】:2009-09-04 17:38:55
【问题描述】:

我正在阅读算法设计手册第二版,这是一个练习题。引用问题

编译器的常见问题和 文本编辑器正在确定是否 字符串中的括号是 平衡和正确嵌套。为了 例如,字符串 ((())())() 包含正确嵌套的对 括号,其中字符串 )()( 也不要。给出一个算法 如果字符串包含,则返回 true 正确嵌套和平衡 括号,否则为假。 要获得完整的信用,请确定职位 第一个有问题的括号如果 字符串没有正确嵌套并且 平衡。

问题属于堆栈、队列和列表类别。这是我用 C# 写的。

const char LeftParenthesis = '(';
const char RightParenthesis = ')';
bool AreParenthesesBalanced(string str, out int errorAt)
{
    var items = new Stack<int>(str.Length);
    errorAt = -1;
    for (int i = 0; i < str.Length; i++)
    {
        char c = str[i];
        if (c == LeftParenthesis)
            items.Push(i);
        else if (c == RightParenthesis)
        {
            if (items.Count == 0)
            {
                errorAt = i + 1;
                return false;
            }
            items.Pop();
        }
    }
    if (items.Count > 0)
    {
        errorAt = items.Peek() + 1;
        return false;
    }
    return true;
}

这很好用。但我不确定这是解决这个问题的正确方法。欢迎任何更好的想法。

【问题讨论】:

  • 可以查看refactormycode.com,它更适合这类事情。
  • 是的,这是非常正确的方法,也用于解析数学表达式。

标签: c# algorithm


【解决方案1】:

我认为这是目的,但实际上,如果您只处理括号,您只需要减少和增加一个计数器。如果您正在处理方括号、尖括号、花括号或任何您想使用的字符配对,您将需要一个堆栈。

您也可以使用列表,将头元素拉开然后拉开,但实际上堆栈可能无论如何都实现为列表 - 至少它在 ocaml 中。

【讨论】:

  • 是的。在这种情况下,简单的实现是增加和减少一个变量。但要求是使用任何数据结构。感谢您的帮助。
  • 一个简单的计数器实现只会检查最后的值是否为零。要回答这个问题,如果它变成负数,您需要立即失败,并且如果它最终是正数,还需要确定第一个过度打开。我认为这不会比 Appu 显示的算法更容易。至于数据结构,无论是否实现为链表,它都将是一个堆栈。事实上,它可以在与字符串一样大的数组上正常工作。或者,在递归示例中,我们使用真正的堆栈。
  • @Appu:它没有通过第二部分,即识别有问题的括号。
  • @Steven:它找到第一个有问题的括号索引并分配给 errorAt 变量。
  • @Steven:这一章不是关于递归的,这就是我没有提出它的原因。此外,这里的字符串是数组,递归没有任何好处,因为你会随身携带一个计数器来访问字符串元素。
【解决方案2】:
    static public bool CheckForBalancedBracketing(string IncomingString)
    {
    /*******************************************************************
     * The easiest way to check for balanced bracketing is to start    *
     * counting left to right adding one for each opening bracket, '(' *
     * and subtracting 1 for every closing bracket, ')'.  At the end   *
     * the sum total should be zero and at no time should the count    *
     * fall below zero.                                                *
     *                                                                 *
     * Implementation:  The bracket counting variable is an unsigned   *
     * integer and we trap an overflow exception.  This happens if the *
     * unsigned variable ever goes negative.  This allows us to abort  *
     * at the very first imbalance rather than wasting time checking   *
     * the rest of the characters in the string.                       *
     *                                                                 *
     * At the end all we have to do is check to see if the count       *
     * is equal to zero for a "balanced" result.                       *
     *                                                                 *
     *******************************************************************/
        const char LeftParenthesis = '(';
        const char RightParenthesis = ')';
        uint BracketCount = 0;

        try
        {
            checked  // Turns on overflow checking.
            {
                for (int Index = 0; Index < IncomingString.Length; Index++)
                {
                    switch (IncomingString[Index])
                    {
                        case LeftParenthesis:
                            BracketCount++;
                            continue;
                        case RightParenthesis:
                            BracketCount--;
                            continue;
                        default:
                            continue;
                    }  // end of switch()

                }
            }
        }

        catch (OverflowException)
        {
            return false;
        }

        if (BracketCount == 0)
        {
            return true;
        }

        return false;

    }  // end of CheckForBalancedBracketing()

【讨论】:

  • 考虑输入为: ')))(((' 的情况。根据您的算法,这不会返回 true,但这是不平衡的
  • @AlwaysNull - 不,这会导致 OverflowException,因为当检测到第一个 RightParenthesis 时,BracketCount-- 会导致 uint 设置为 -1。
【解决方案3】:

这适用于(){}[] 的组合。

还检测以下错误:([)])[]()()(、...

bool isWellFormatted(string line)
{           
        Stack<char> lastOpen = new Stack<char>();
        foreach (var c in line)
        {               
            switch (c)
            {
                case ')':
                    if (lastOpen.Count == 0 || lastOpen.Pop() != '(') return false;
                    break;
                case ']':
                    if (lastOpen.Count == 0 || lastOpen.Pop() != '[' ) return false;
                    break;
                case '}':
                    if (lastOpen.Count == 0 || lastOpen.Pop() != '{') return false;
                    break;
                case '(': lastOpen.Push(c); break;
                case '[': lastOpen.Push(c); break;
                case '{': lastOpen.Push(c); break;
            }                                                                       
        }
        if (lastOpen.Count == 0) return true;
        else return false;
    }

【讨论】:

    【解决方案4】:
    1. 从输入字符串中删除所有非'(' 和 -')' 字符。这只会给你一个字符串 '(' 和 ')'。

    2. 如果字符串长度为奇数,则返回false。

    3. 否则,开始阅读我们的字符串,为每个 '(' 添加 +1 到“签名”,为每个 ')' 添加 -1;如果此签名是否定的,则返回 false。

    4. 返回真。

    【讨论】:

      【解决方案5】:
      using System;
      class Solution
      {
          public int solution(string S)
          {
              int x1 = 0;
              int x2 = 0;
              for (int i = 0; i < S.Length; i++)
              {
                  if (S[i] == ')')
                      if (x1 <= 0) return 0;
                      else x1--;
                  else if (S[i] == '(')
                      x1++;
              }
              if (x1 == 0)
                  return 1;
              else
                  return 0;
          }
      }
      

      【讨论】:

        【解决方案6】:

        我会使用队列和堆栈来检查打开和关闭匹配

                var dictionary = new Dictionary<string, string>() {
                    { "{", "}" },
                    {"[", "]" },
                    {"(",")" }
                };
        
        
                var par = "{()}";
                var queue = new Queue();
                var stack = new Stack();
        
                bool isBalanced = true;
        
                var size = par.ToCharArray().Length;
        
                if(size % 2 != 0)
                {
                    isBalanced = false;
                }
                else
                {
                    foreach (var c in par.ToCharArray())
                    {
                        stack.Push(c.ToString());
                        queue.Enqueue(c.ToString());
                    }
        
                    while (stack.Count > size/2 && queue.Count > size/2)
                    {
                        var a = (string)queue.Dequeue();
                        var b = (string)stack.Pop();
        
                        if (dictionary.ContainsKey(a) && b != dictionary[a])
                        {
                            isBalanced = false;
        
                        }
        
        
                    }
        
        
                }
        
                Console.WriteLine(isBalanced?"balanced!":"Not Balanced");
        
                Console.ReadLine();
        

        例如,在第一次迭代中,a ='{' 和 b ='}' 所以你检查它是否平衡

        【讨论】:

          【解决方案7】:

          我让它更通用一点 爪哇

          public static boolean isBalanced(String expression)
          {
              // pairs at the same index
              List<Character> openers = Arrays.asList('{', '(', '[');
              List<Character> closers = Arrays.asList('}', ')', ']');
              char[] exp = expression.toCharArray();
              Stack<Character> stack = new Stack<>();
              for(Character character: exp){
                  if (openers.contains(character))
                      stack.push(character);
                  if(closers.contains(character)){
                      if (stack.empty())
                          return false;
                      //matching pair should be at the same index
                      Character opener = stack.pop();
                      int openerIndex = openers.indexOf(opener);
                      int closerIndex = closers.indexOf(character);
                      if (openerIndex != closerIndex)
                          return false;
                  }
              }
              return stack.empty();
          }
          

          【讨论】:

            【解决方案8】:

            时间顺序 O(n) 和空间顺序 O(1)

            public static bool IsBalanced(string input)
            {
               int count = 0;
               for (int i = 0; i < input.Length; i++)
               {
                   if (input[i] == '(') count++;
                   if (input[i] == ')') count--;
                   if (count < 0) return false;
                }
                if (count == 0) return true;
                return false;
            }
            

            【讨论】:

              【解决方案9】:

              正如 TheVillageIdiot 所说,没关系。您也可以递归地实现它,这可能更优雅,也可能不会。最后,您可能希望要求匹配的括号在它们之间包含有效的内容,以便允许“(a)”而不是“()”。

              【讨论】:

              • 这个答案有 2 票反对,有 0 条解释。我感觉到不平衡。因此,如果您有理由拒绝投票,我建议您分享它。
              【解决方案10】:

              为什么有一个返回值和一个输出参数给出相同的信息?

              您可以返回一个 int:-1 = 平衡,否则返回错误的索引。

              【讨论】:

              • 我想过。但有些我觉得这种模式更适合这里。感谢您的帮助。
              • @Bill:那么,您可能想调用 FindInvalidParenthesis 方法。然后它在失败时返回 -1 并在成功时返回索引是有意义的。话虽如此,这个 Try* 接口在将实际值作为输出参数传递时返回成功标志,在很多方面都更简洁。
              • 这是一个很好的观点,根据这个建议可以更改名称。但是必须设置两个值并检查两个值而不是一个值来确定函数的结果?恐怕我看不出它有多干净。
              【解决方案11】:
              int i;
              int len; 
              char popped;
              stack<char> st;
              string a = "({<<";
              len = a.length();
              
              for(i=0;i<len;i++)
              {
                  if(a[i] == '<' || a[i] == '(' || a[i] == '[' || a[i] == '{')
                  {
                      st.push(a[i]); 
                      continue;
                  }
                  if(a[i] == '>' || a[i] == ')' || a[i] == ']' || a[i] == '}')
                  {
                      if(st.empty())
                      {
                          cout << "stack is empty, when popped , not balanced" << endl;
                          return 0;
                      }
                      else
                      {
                          popped = st.top(); 
                          st.pop();
                          if (!((a[i] == '>' && popped == '<') || (a[i] == ')' && popped == '(') || (a[i] == '}' && popped == '{') || (a[i] == '>' && popped == '<'))) //ok
                          {
                              cout << "not balanced on character" + std::string(1,a[i]) << endl;
                              return 0;
                          }
                      }
              
                  }
              
              }
              if(st.empty())
              {
                  cout << "balanced" << endl;
              }
              else
              {
                  cout << "not balanced stack not empty" << endl;
              }
              

              【讨论】:

                【解决方案12】:
                Checking balanced parentheses
                package parancheck;
                
                import java.util.EmptyStackException;
                import java.util.Stack;
                
                public class ParnCheck 
                {
                    public static void main(String args[])
                    {
                        int pu = 0;
                        int po = 0;
                        String str = "()())";
                        System.out.println(str); 
                        Stack st = new Stack();
                       for(int i=0; i<str.length();i++)
                       {
                        if(str.charAt(i)=='(')
                        {
                           doPush(st, str.charAt(i));
                           pu++;
                         }
                        else 
                        {
                             try
                              {
                                doPop(st);
                              }
                             catch(EmptyStackException e)
                              {
                                System.out.println("");
                              }
                              po++;
                        }
                     }
                
                
                       if(pu == po)
                       {
                           System.out.println("Correct");
                       }
                       else
                       {
                           System.out.println("Wrong");
                       }
                
                    }
                    static void doPush(Stack st,char ch)
                    {
                        st.push(ch);
                    }
                    static void doPop(Stack st)
                    {
                        char c = (char)st.pop();
                    }
                }
                

                【讨论】:

                  【解决方案13】:
                  import java.util.Stack;
                  
                  public class CheckBalancedParenthesis {
                  
                      public static void main (String args[]){
                          CheckBalancedParenthesis checker = new CheckBalancedParenthesis();
                          System.out.println(checker.checkBalancedParenthesis("{}}{}{}{}{}"));
                      }
                  
                      public boolean checkBalancedParenthesis(String pattern){
                          Stack stack = new Stack();
                          for(int i = 0; i < pattern.length();i++){
                              char c = pattern.charAt(i);
                              if(c == '{'){
                                  stack.push(c);
                              }else if (c == '}'){
                                  if(!stack.isEmpty()){
                                      stack.pop();
                                  } else{
                                      System.out.println("Error at - " + i);
                                      return false;
                                  }
                              }
                          }
                          return stack.isEmpty();
                      }
                  }
                  

                  【讨论】:

                    【解决方案14】:

                    这是验证括号的简单解决方案:
                    1. 将开始括号和结束括号保留在一个字符串中。
                    2. 遍历我们要验证的对象并检查以下逻辑:

                    a) 如果项目在起始括号中,则将其推入堆栈
                    b)如果项目在结束括号中,将其索引(在结束括号中)与堆栈的顶部项目进行比较 索引(在起始括号中)。
                    c) 如果索引是相同的 POP TOP ITEM OF STACK。否则它的无效字符串。
                    d) 最后一步,检查堆栈是否有item/s,这意味着它的无效字符串。

                        string starters = "({[<";
                        string enders = ")}]>";
                        Stack stack  = new Stack();
                        foreach(char c in txtValue.Text.Trim())
                        {
                            if(starters.Contains(c))
                            {
                                stack.Push(c);
                            }
                            else if (enders.Contains(c))
                            {
                                if (stack.Count > 0)
                                {
                    
                                    if (enders.IndexOf(c) == starters.IndexOf(Convert.ToChar(stack.Peek())))
                                    {
                                        stack.Pop();
                                    }
                                    else
                                    {
                                        lblResult.Text = "Invaluid string";
                                    }
                                }
                            }
                        }
                    
                        if(stack.Count == 0)
                        {
                            lblResult.Text = "Valid string";
                        }
                        else
                        {
                            lblResult.Text = "InValid string";
                        }
                    

                    【讨论】:

                    • 您可以使用String.IndexOf(Char) 的结果作为包含的指示。
                    【解决方案15】:

                    借鉴@Russell 的想法:

                    public class BalancedBrackets
                    {
                        private readonly char[] _leftBrackets = new char[] {'[', '(', '{', '<'};
                        private readonly char[] _rightBrackets = new char[] {']', ')', '}', '>'};
                    
                        public bool IsBalanced(string input)
                        {
                            int count = 0;
                            foreach (var character in input.ToCharArray())
                            {
                                if (_leftBrackets.Contains(character)) count++;
                                if (_rightBrackets.Contains(character)) count--;
                            }
                            return count == 0;
                        }
                    }
                    

                    【讨论】:

                    【解决方案16】:

                    这是使用 System.Linq 的 C# 单行代码:

                    expression.Aggregate(0, (state, ch) => state == -1 ? -1 : state + (ch == '(' ? 1 : ch == ')' ? -1 : 0)) == 0
                    

                    它利用了字符串实际上是 IEnumerable 字符这一事实,因此我们可以在其上运行 Aggregate 函数。当我们遇到 '(' 字符时,我们将计数器增加 1,并在 ')' 字符上将其减少 1。一旦我们达到 -1 的负值,我们就会停留在那里以指示无效状态。

                    它没有实现任何提前退出,因此它会比这里介绍的大多数实现慢,但也许有人会发现它很有用:)

                    【讨论】:

                      【解决方案17】:

                      这应该可行:

                      public class Balanced {
                          public static boolean isbalanced(String input) {
                              int open = 0;
                              int close = 0;
                              for (int i=0; i< input.length();i++) {
                                  switch (input.charAt(i)) {
                                      case '{':
                                      case '(':
                                      case '[':
                                          open++;
                                          break;
                                      case '}':
                                      case ')':
                                      case ']':
                                          close++;
                                  default:
                                      break;
                                  }
                              }
                              if (open == close){
                                  return true;
                              }
                              else {
                                  return false;
                              }
                          }
                      
                      public static void main(String args[]) {
                          System.out.println(Balanced.isbalanced("()"));
                      }
                      }
                      

                      【讨论】:

                        【解决方案18】:

                        C# 7 左右现在也有元组。您不再需要 out 参数。 正如许多其他人指出的那样,不需要堆栈或队列等。
                        只需一个余额计数器就足够了。

                            -- Checks the balance of braces in a string.
                            -- Error case 1: More closes than open. We can identify the first culprit by index.
                            -- Error case 2: More opens than closes. We return the length of the String, 
                            --               indicating that there are closed braces missing.
                            -- Good case: As many opens as closes. We return (True,Nothing)
                            checkBraces :: String -> (Bool,Maybe Int)
                            checkBraces [] = (True,Nothing)
                            checkBraces s =
                                let (balance,offender) = foldl account (0,-1) $ zip [0..] s in
                                if balance == 0 
                                    then (True,Nothing) 
                                    else (False,Just $ if -1 == offender then length s else offender)
                                where
                                    account :: (Int,Int) -> (Int, Char) -> (Int,Int)
                                    account acc@(_,off) _ | off /= -1 = acc     -- Once there was an error we stop looking what happens.
                                    account acc@(b,off) (i,'(') = (b+1,off)     -- One more open brace.
                                    account (b,off) (i,')')                     -- One more closed brace.
                                            | b <= 0 = (b-1,i)                  -- Ouch. We closed more than we opened!
                                            | otherwise = (b-1,off)             -- Okay.
                                    account acc (i,_) = acc                     -- Some other character (Not in ['(',')'])
                        
                        
                            testCases =
                                [ ("",True)
                                , ("(",False)
                                , (")",False)
                                , ("))((",False)
                                , ("()()",True)
                                , ("(()))",False)
                                ]
                        
                            test = 
                                all ((==) True) . fmap testOne $ testCases
                                where
                                    testOne (tc,expected) =
                                        let (actual,_) = checkBraces tc in
                                        actual == expected
                        
                        

                        旁注:这里的 Haskell 语法高亮需要一些改进,对吧? :)

                        【讨论】:

                          猜你喜欢
                          • 2018-06-23
                          • 2011-11-04
                          • 2013-12-28
                          • 2012-09-14
                          • 2013-02-03
                          • 2017-03-07
                          • 2022-08-13
                          • 2019-11-01
                          • 2021-09-20
                          相关资源
                          最近更新 更多