Last active
October 4, 2024 19:35
-
-
Save jphsd/5735a3006d09f988ed1f1df29d351488 to your computer and use it in GitHub Desktop.
A recent interview question was to check a string for balanced brackets - here's my answer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import "fmt" | |
/** | |
* Given a string expression, write a program to examine whether the pairs and the orders of | |
* "{", "}", "(", ")", "[", "]" are correct (all other chars are ignored). If they are correct, it is balanced. | |
* If balanced, just return "Balanced". | |
* If not balanced, return "Not Balanced: <problem-index>", where problem-index is the index of the first bracket | |
* that caused un-balancing. | |
* | |
* Examples: | |
* Input: exp = "[()] {} { [()()] () }" | |
* Output: Balanced | |
* | |
* Input: exp = "[(abc] x)" | |
* Output: Not Balanced: 1 | |
* | |
* Input: exp = "[]()}" | |
* Output: Not Balanced: 4 | |
* | |
* Input: exp = "(([}]" | |
* Output: Not Balanced: 3 | |
*/ | |
type TestCase struct { | |
Name string | |
Test string | |
Outc string | |
} | |
var cases = []TestCase{ | |
TestCase{"Test1", "[()] {} { [()()] () }", "Balanced"}, | |
TestCase{"Test2", "[(abc] x)", "Not Balanced: 1"}, | |
TestCase{"Test3", "[]()}", "Not Balanced: 4"}, | |
TestCase{"Test4", "(([}]", "Not Balanced: 3"}, | |
TestCase{"Testa", "[][[][][]]", "Balanced"}, | |
TestCase{"Testb", "[][[][[][]]", "Not Balanced: 2"}, | |
TestCase{"Testc", "[({a(abc] x)", "Not Balanced: 1"}, | |
} | |
func main() { | |
for _, c := range cases { | |
fmt.Println(c.Name + " " + c.Test) | |
Balanced(c.Test) | |
fmt.Println("Expected " + c.Outc + "\n") | |
} | |
} | |
type Item struct { | |
Position int | |
Value rune | |
} | |
func Balanced(input string) { | |
items := []Item{} | |
epos := -1 | |
for i, c := range input { | |
switch c { | |
// Openers | |
case '[': | |
fallthrough | |
case '(': | |
fallthrough | |
case '{': | |
items = append(items, Item{i, c}) | |
// Closers | |
case ']': | |
fallthrough | |
case ')': | |
fallthrough | |
case '}': | |
n := len(items) | |
if n == 0 { | |
epos = i | |
break | |
} | |
n-- | |
top := items[n] | |
if !match(top.Value, c) { | |
// Walk back through items until either find match, in which case item after it is the issue | |
// or we reach the beginning of the list and it was a naked close | |
j := findPrev(c, items) | |
if j == -1 { | |
epos = i | |
} else { | |
epos = items[j+1].Position | |
} | |
break | |
} | |
items = items[:n] | |
} | |
if epos != -1 { | |
break | |
} | |
} | |
// Finished string parsing | |
if epos != -1 { | |
fmt.Printf("Not Balanced: %d\n", epos) | |
} else if len(items) != 0 { | |
fmt.Printf("Not Balanced: %d\n", items[0].Position) | |
} else { | |
fmt.Println("Balanced") | |
} | |
} | |
func match(c1, c2 rune) bool { | |
switch c1 { | |
case '[': | |
return c2 == ']' | |
case '(': | |
return c2 == ')' | |
case '{': | |
return c2 == '}' | |
} | |
return false | |
} | |
func findPrev(c rune, items []Item) int { | |
n := len(items) | |
if n == 0 { | |
return -1 | |
} | |
switch c { | |
case ']': | |
c = '[' | |
case ')': | |
c = '(' | |
case '}': | |
c = '{' | |
} | |
for i := n - 1; i > -1; i-- { | |
if items[i].Value == c { | |
return i | |
} | |
} | |
return -1 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Play with it here