exercism/go/pov/pov.go
Brian Buller 42d8211402 Changing it into a graph
Since that's what they actually want, even though the exercise says it's
a tree
2017-08-24 11:16:04 -05:00

137 lines
2.6 KiB
Go

package pov
const testVersion = 2
// A Graph is just a collection of Nodes
type Node struct {
label string
connections []Node
}
type Graph struct {
nodes []Node
}
func New() *Graph {
return new(Graph)
}
func (g *Graph) AddNode(lbl string) {
if g.getNode(lbl) == nil {
g.nodes = append(g.nodes, Node{label: lbl})
}
}
func (g *Graph) getNode(lbl string) *Node {
for i := range g.nodes {
if g.nodes[i].label == lbl {
return &g.nodes[i]
}
}
return nil
}
func (g *Graph) AddArc(fr, to string) {
frN := g.getNode(fr)
if frN == nil {
g.AddNode(fr)
frN = g.getNode(fr)
}
toN := g.getNode(to)
if g.getNode(to) == nil {
g.AddNode(to)
toN = g.getNode(to)
}
frN.connections = append(frN.connections, *toN)
}
func (g *Graph) ArcList() []string {
var ret []string
for _, k := range g.nodes {
for _, l := range k.connections {
ret = append(ret, k.label+" -> "+l.label)
}
}
return ret
}
// getPath returns the shortest path from 'fr' to 'to'
func (g *Graph) getPath(fr, to string) []string {
if fr == to {
return []string{fr}
}
st := g.getNode(fr)
if st == nil {
// Couldn't find the starting node
return []string{}
}
var valPaths [][]string
for i := range st.connections {
if st.connections[i].label == to {
return []string{fr, to}
}
tst := g.getPath(st.connections[i].label, to)
if len(tst) > 0 {
valPaths = append(valPaths, tst)
}
}
if len(valPaths) == 0 {
// No valid paths found
return []string{}
}
var ret []string
for i := range valPaths {
if len(valPaths[i]) < len(ret) || len(ret) == 0 {
ret = valPaths[i]
}
}
return ret
}
func (g *Graph) ChangeRoot(oldRoot, newRoot string) *Graph {
pth := g.getPath(oldRoot, newRoot)
if len(pth) == 0 {
// No valid path
return g
}
for oldRoot != newRoot {
// We just keep swapping the first two in pth until we're done
if len(pth) >= 2 {
n := g.getNode(pth[1])
if n == nil {
return g
}
n = n.copy()
p := g.getNode(pth[0])
if p == nil {
return g
}
p = p.copy()
p.removeConnection(n.label)
n.connections = append(n.connections, *p)
oldRoot = n.label
}
}
return g
}
func (n *Node) removeConnection(to string) {
for i, k := range n.connections {
if k.label == to {
copy(n.connections[i:], n.connections[i+1:])
n.connections[len(n.connections)-1] = Node{}
n.connections = n.connections[:len(n.connections)-1]
}
}
}
// Node.copy() returns a new node that is a copy of this node
func (n *Node) copy() *Node {
ret := Node{label: n.label}
for _, k := range n.connections {
cp := k.copy()
ret.connections = append(ret.connections, *cp)
}
return &ret
}