The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Use a last-in, first-out stack. Scan the string from left to right, push each opening bracket, and require every closing bracket to match the opener currently on top of the stack. A closing bracket with no opener, a wrong bracket type, or any opener left at the end makes the string invalid.
The implementation below validates (), [], and {} in one pass. It also makes the policy for non-bracket characters explicit, which is essential when inputs such as a(b) are possible.
The stack algorithm
Balanced delimiters are nested, so the most recently opened bracket must be the first one closed. That is exactly last-in, first-out behavior. Python’s list type provides clear append() and pop() operations for this purpose.
- Read characters from left to right.
- Push
(,[, or{onto the stack. - For
),], or}, check that the stack is nonempty and that its top is the corresponding opener. - Return
Falseimmediately for an empty stack or a mismatch; otherwise pop the opener. - After the scan, return
Trueonly if the stack is empty.
A complete Python implementation
def valid_parentheses(text: str) -> bool:
matching = {")": "(", "]": "[", "}": "{"}
stack: list[str] = []
for char in text:
if char in "([{":
stack.append(char)
elif char in matching:
if not stack or stack[-1] != matching[char]:
return False
stack.pop()
else:
raise ValueError(f"unexpected character: {char!r}")
return not stack
This function accepts a string containing only brackets, or a string in which non-bracket characters are treated as invalid. That choice is deliberate. If your input contract allows ordinary text, change the final else branch to continue (or simply do nothing) so that letters, spaces, and punctuation are ignored.
#1 Best Overall
def valid_parentheses_in_text(text: str) -> bool:
matching = {")": "(", "]": "[", "}": "{"}
stack: list[str] = []
for char in text:
if char in "([{":
stack.append(char)
elif char in matching:
if not stack or stack[-1] != matching[char]:
return False
stack.pop()
# Other characters are intentionally ignored.
return not stack
Why each failure case is detected
Wrong bracket type
For (], the stack contains ( when ] arrives. The mapping says that ] must close [, so the function returns False.
Wrong nesting order
For ([)], the stack is ['(', '['] when ) is read. The top is [, not (, so the crossing close is rejected. Properly nested text such as ([{}]) always closes the top item first.
Closing before opening
For )(, the first character is a close while the stack is empty. Returning immediately prevents an invalid access and identifies the error at the earliest possible position.
Unclosed openings
For ((, no mismatch occurs during the scan, but two openers remain. return not stack therefore returns False.
Rank #2
Empty input
The empty string returns True under the usual definition of a balanced sequence: it contains no unmatched brackets. If your application requires at least one bracket, add a separate nonempty-input check.
Examples and expected results
| Input | Result | Reason |
|---|---|---|
()[]{} |
True |
Every opener is closed by the matching type. |
([{}]) |
True |
Nested closures occur in reverse opening order. |
(] |
False |
The closing type does not match. |
([)] |
False |
The nesting order crosses. |
)( |
False |
A close appears with no opener. |
(( |
False |
Openers remain after scanning. |
"" |
True |
Empty sequences are balanced by the standard definition. |
For the strict function, valid_parentheses("a(b)") raises ValueError. For the text-oriented version, valid_parentheses_in_text("a(b)") returns True. Choose one contract and document it at the boundary of your program.
Complexity and data-structure choice
With n characters, the scan takes O(n) time because each character is examined once. The stack uses O(n) auxiliary space in the worst case, when every character is an opener. It uses less space for mostly closed or non-bracket text.
A list is the default recommendation because all operations happen at one end and the intent is obvious. collections.deque is also valid and provides approximately O(1) appends and pops at either end. Use a deque when the surrounding parser already needs efficient operations on both ends; it does not make this one-ended algorithm faster or simpler.
Recommended Free Tools
from collections import deque
def valid_with_deque(text: str) -> bool:
matching = {")": "(", "]": "[", "}": "{"}
stack: deque[str] = deque()
for char in text:
if char in "([{":
stack.append(char)
elif char in matching:
if not stack or stack[-1] != matching[char]:
return False
stack.pop()
else:
raise ValueError(f"unexpected character: {char!r}")
return not stack
Testing the validator
Test successful nesting, each failure mode, and the input policy. A small table-driven test catches regressions without obscuring the algorithm.
cases = {
"()[]{}": True,
"([{}])": True,
"(]": False,
"([)]": False,
")(": False,
"(": False,
"": True,
}
for source, expected in cases.items():
actual = valid_parentheses(source)
assert actual == expected, (source, actual, expected)
try:
valid_parentheses("a(b)")
except ValueError:
pass
else:
raise AssertionError("strict mode should reject non-bracket characters")
Useful properties for larger test suites
- Adding a correctly matched pair around a valid string should preserve validity.
- Changing one closing bracket to a different type should make the result invalid.
- Removing one bracket from a valid string should leave an unmatched opener or closer.
- Very long runs of opening brackets should complete without recursion, because the algorithm is iterative.
Common implementation mistakes
Checking only the counts
Counting opening and closing brackets is insufficient. (] has one opener and one closer but is not valid; order and type must be checked with a stack.
Using the first opener instead of the latest
Removing from the front of a list tests the wrong nesting rule and can also make operations needlessly expensive. Use stack[-1] and stack.pop().
Forgetting the final stack check
A scan that rejects mismatches but always returns True incorrectly accepts ( and ((. The final emptiness check is mandatory.
Indexing an empty stack
Check not stack before reading stack[-1]. This handles premature closing brackets safely.
Silently choosing a character policy
Ignoring non-bracket characters may be right for source-code fragments, while rejecting them may be right for a delimiter-only parser. Neither policy is universally correct; expose it through separate functions or an explicit option.
Adapting the function for real parsers
Returning an error position
For diagnostics, return the index and a message instead of only a Boolean. The same loop can report the position of a premature close or mismatch; after the loop, any remaining opener can be reported as unclosed. Keep the Boolean function for callers that only need acceptance.
Supporting additional delimiter pairs
Extend the opening-character test and the matching dictionary together. Every closing symbol must map to exactly one opener. If delimiters can be escaped or occur inside quoted strings, handle those lexical rules before applying the simple stack algorithm; quotes and escape sequences are not parentheses and cannot be safely ignored by this function.
Best Value
Streaming input
The loop only needs the current character and the stack, so it can be moved into a function that consumes chunks from a file or network stream. Preserve the same stack between chunks and perform the emptiness check only after the final chunk.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If your goal is to capture a webpage rather than validate delimiters, ScreenshotNeo is the first service to try: it removes consent banners, popups, and chat widgets before capture, bills only clean shots, and has a $5 paid plan for 3,000 shots.
One GET request returns a PNG, JPEG, WebP, or PDF. The API also reports page and billing outcomes in X-Page-Verdict and X-Billed headers.
curl -G "https://api.screenshotneo.com/v1/shot"
-d access_key=YOUR_API_KEY
--data-urlencode url=https://stripe.com
-o shot.webp
Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', data));
See the parameter reference and all 63 capture options in the ScreenshotNeo documentation. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Cookie banners, newsletter popups, and chat widgets can be removed before the shot; bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing. The Free plan includes 1,000 shots per month with no card, and paid plans start at $5 for 3,000 shots. Sign up free.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesWhen this algorithm is not enough
The validator checks delimiter balance only. It does not parse Python syntax, determine whether a bracket appears inside a string literal, or understand comments. For complete Python source validation, use Python’s parser after any preprocessing your application requires. Keep this small stack routine for token streams, configuration formats, interview exercises, and other contexts where bracket characters are the defined input.
Frequently Asked Questions
Should an empty string be considered valid?
Yes, under the standard balanced-sequence definition. Add a separate nonempty check only when your application’s input contract requires at least one bracket.
Why not solve this with a regular expression?
Arbitrarily nested pairs require remembering an unbounded sequence of openers. A stack directly models that requirement and reports mismatches as soon as they occur.
Can I validate only parentheses instead of three bracket types?
Yes. Replace the opening-character test with if char == '(' and the mapping with {')': '('}; the push, match, pop, and final-empty checks remain the same.
Free tools Windows power users keep installed
One-click scans. No signup required.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

