Skip to content

Instantly share code, notes, and snippets.

@jphsd
Last active October 4, 2024 19:35
Show Gist options
  • Save jphsd/5735a3006d09f988ed1f1df29d351488 to your computer and use it in GitHub Desktop.
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
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
}
@jphsd
Copy link
Author

jphsd commented Oct 4, 2024

Play with it here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment