Valid Parentheses in O(n) Time Complexity using JavaScript


Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.

An input string is valid if:

  1. Open brackets must be closed by the same type of brackets.
  2. Open brackets must be closed in the correct order.

Example 1:

Input: s = "()"
Output: true

Example 2:

Input: s = "()[]{}"
Output: true

Example 3:

Input: s = "(]"
Output: false

Example 4:

Input: s = "([)]"
Output: false

Example 5:

Input: s = "{[]}"
Output: true

Constraints:

  • 1 <= s.length <= 104
  • s consists of parentheses only '()[]{}'.

Understanding the Problem

The core challenge of this problem is to ensure that every opening bracket has a corresponding closing bracket and that they are correctly nested. This problem is significant in various applications such as parsing expressions in compilers, validating mathematical expressions, and ensuring the correctness of nested structures in programming languages.

Potential pitfalls include mismatched brackets, incorrect nesting, and unbalanced brackets.

Approach

To solve this problem, we can use a stack data structure. The stack helps us keep track of the opening brackets and ensures that they are closed in the correct order.

Here is a step-by-step approach:

  1. Initialize an empty stack.
  2. Iterate through each character in the string.
  3. If the character is an opening bracket, push it onto the stack.
  4. If the character is a closing bracket, check if the stack is not empty and the top of the stack is the corresponding opening bracket. If so, pop the stack. Otherwise, return false.
  5. After iterating through the string, check if the stack is empty. If it is, return true; otherwise, return false.

Algorithm

Let's break down the algorithm step-by-step:

  1. Create a stack to keep track of opening brackets.
  2. Use a map to store the pairs of matching brackets.
  3. Iterate through the string:
    • If the character is an opening bracket, push it onto the stack.
    • If the character is a closing bracket, check if the stack is not empty and the top of the stack is the corresponding opening bracket. If so, pop the stack. Otherwise, return false.
  4. After the loop, check if the stack is empty. If it is, return true; otherwise, return false.

Code Implementation


// Function to check if the input string is valid
function isValid(s) {
    // Map to store the pairs of matching brackets
    const bracketMap = {
        '(': ')',
        '{': '}',
        '[': ']'
    };
    
    // Stack to keep track of opening brackets
    const stack = [];
    
    // Iterate through each character in the string
    for (let char of s) {
        // If the character is an opening bracket, push it onto the stack
        if (bracketMap[char]) {
            stack.push(char);
        } else {
            // If the character is a closing bracket, check if the stack is not empty
            // and the top of the stack is the corresponding opening bracket
            const top = stack.pop();
            if (bracketMap[top] !== char) {
                return false;
            }
        }
    }
    
    // After iterating through the string, check if the stack is empty
    return stack.length === 0;
}

// Test cases
console.log(isValid("()")); // true
console.log(isValid("()[]{}")); // true
console.log(isValid("(]")); // false
console.log(isValid("([)]")); // false
console.log(isValid("{[]}")); // true

Complexity Analysis

The time complexity of this algorithm is O(n) because we iterate through the string once. The space complexity is also O(n) because, in the worst case, we might need to push all opening brackets onto the stack.

Edge Cases

Some potential edge cases include:

Examples:

isValid("") // true
isValid("(((((") // false
isValid(")))))") // false

Testing

To test the solution comprehensively, we should include a variety of test cases:

We can use testing frameworks like Jest or Mocha to automate the testing process.

Thinking and Problem-Solving Tips

When approaching such problems, it's essential to:

Conclusion

In this blog post, we discussed how to solve the "Valid Parentheses" problem using a stack-based approach. We covered the problem definition, approach, algorithm, code implementation, complexity analysis, edge cases, and testing. Understanding and solving such problems is crucial for improving problem-solving skills and preparing for technical interviews.

Additional Resources

For further reading and practice problems, consider the following resources: