mediumJS ES6+#107

Validar paréntesis (balanced)

Prompt

Given a string of parentheses (){}[], determine if the order is valid (balanced). Every opening bracket must have a matching closing bracket in the correct order.

Solution

function isValidBrackets(str) {
  const pairs = { ')': '(', '}': '{', ']': '[' }
  const stack = []
  for (const c of str) {
    if ('([{'.includes(c)) stack.push(c)
    else if (pairs[c] !== stack.pop()) return false
  }
  return stack.length === 0
}
Mentor's take

This is the introductory stack problem, and the pattern matters far beyond brackets: any "most recent open thing must close first" structure — nested tags, expression parsers, editor undo scopes — is LIFO, and a stack is its data structure. Push every opener; on a closer, pop and demand it matches. O(n) time, O(n) space worst case (all openers).

Why counting fails: tracking three counters catches "(()" but not "([)]" — counts balance while nesting order is wrong. Order is exactly what the stack encodes and counters throw away. That's the trap the interviewer sets with the "([)]" test.

Two ends of the string carry their own bugs:

  • Closer on an empty stack: stack.pop() returns undefined, which fails the pairs[c] !== ... comparison — so "]" correctly returns false with no explicit emptiness check. Know why that works; it looks accidental otherwise.
  • Leftover openers: "(((" survives the loop, so the final answer must be stack.length === 0, not true.

The pairs map (closer → opener) keeps the loop to one comparison instead of a three-way conditional.

Red flag: returning true after the loop without checking the stack is empty — unclosed openers pass silently. Counter-based solutions are the deeper miss: they don't model nesting at all.

Say it: "Brackets are LIFO, so I push openers and pop on closers — mismatch or a non-empty stack at the end means invalid, all in O(n)."