Initial upload
This commit is contained in:
37
go/tree-building/.exercism/config.json
Normal file
37
go/tree-building/.exercism/config.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"blurb": "Refactor a tree building algorithm.",
|
||||
"authors": [
|
||||
"pminten"
|
||||
],
|
||||
"contributors": [
|
||||
"alebaffa",
|
||||
"bitfield",
|
||||
"Daveed9",
|
||||
"devillexio",
|
||||
"dvrkps",
|
||||
"ekingery",
|
||||
"ferhatelmas",
|
||||
"hilary",
|
||||
"jeffguorg",
|
||||
"kytrinyx",
|
||||
"leenipper",
|
||||
"object88",
|
||||
"petertseng",
|
||||
"robphoenix",
|
||||
"sebito91",
|
||||
"soniakeys",
|
||||
"tbrisker",
|
||||
"tleen"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"tree_building.go"
|
||||
],
|
||||
"test": [
|
||||
"tree_building_test.go"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.go"
|
||||
]
|
||||
}
|
||||
}
|
1
go/tree-building/.exercism/metadata.json
Normal file
1
go/tree-building/.exercism/metadata.json
Normal file
@@ -0,0 +1 @@
|
||||
{"track":"go","exercise":"tree-building","id":"41954e1ca66440f899c639278975f0d2","url":"https://exercism.org/tracks/go/exercises/tree-building","handle":"halfdan","is_requester":true,"auto_approve":false}
|
40
go/tree-building/HELP.md
Normal file
40
go/tree-building/HELP.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
To run the tests run the command `go test` from within the exercise directory.
|
||||
|
||||
If the test suite contains benchmarks, you can run these with the `--bench` and `--benchmem`
|
||||
flags:
|
||||
|
||||
go test -v --bench . --benchmem
|
||||
|
||||
Keep in mind that each reviewer will run benchmarks on a different machine, with
|
||||
different specs, so the results from these benchmark tests may vary.
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit tree_building.go` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [Go track's documentation](https://exercism.org/docs/tracks/go)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
To get help if you're having trouble, you can use one of the following resources:
|
||||
|
||||
- [How to Write Go Code](https://golang.org/doc/code.html)
|
||||
- [Effective Go](https://golang.org/doc/effective_go.html)
|
||||
- [Go Resources](http://golang.org/help)
|
||||
- [StackOverflow](http://stackoverflow.com/questions/tagged/go)
|
56
go/tree-building/README.md
Normal file
56
go/tree-building/README.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Tree Building
|
||||
|
||||
Welcome to Tree Building on Exercism's Go Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Instructions
|
||||
|
||||
Some web-forums have a tree layout, so posts are presented as a tree. However
|
||||
the posts are typically stored in a database as an unsorted set of records. Thus
|
||||
when presenting the posts to the user the tree structure has to be
|
||||
reconstructed.
|
||||
|
||||
Your job will be to implement the tree building logic for these records. The
|
||||
records only contain an ID number and a parent ID number. The ID number is
|
||||
always between 0 (inclusive) and the length of the record list (exclusive). All
|
||||
records have a parent ID lower than their own ID, except for the root record,
|
||||
which has a parent ID that's equal to its own ID.
|
||||
|
||||
An example tree:
|
||||
|
||||
```text
|
||||
root (ID: 0, parent ID: 0)
|
||||
|-- child1 (ID: 1, parent ID: 0)
|
||||
| |-- grandchild1 (ID: 2, parent ID: 1)
|
||||
| +-- grandchild2 (ID: 4, parent ID: 1)
|
||||
+-- child2 (ID: 3, parent ID: 0)
|
||||
| +-- grandchild3 (ID: 6, parent ID: 3)
|
||||
+-- child3 (ID: 5, parent ID: 0)
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @pminten
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @alebaffa
|
||||
- @bitfield
|
||||
- @Daveed9
|
||||
- @devillexio
|
||||
- @dvrkps
|
||||
- @ekingery
|
||||
- @ferhatelmas
|
||||
- @hilary
|
||||
- @jeffguorg
|
||||
- @kytrinyx
|
||||
- @leenipper
|
||||
- @object88
|
||||
- @petertseng
|
||||
- @robphoenix
|
||||
- @sebito91
|
||||
- @soniakeys
|
||||
- @tbrisker
|
||||
- @tleen
|
3
go/tree-building/go.mod
Normal file
3
go/tree-building/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module tree
|
||||
|
||||
go 1.13
|
12
go/tree-building/tree_building.go
Normal file
12
go/tree-building/tree_building.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package tree
|
||||
|
||||
// Define the Record type
|
||||
struct Node {
|
||||
|
||||
}
|
||||
|
||||
// Define the Node type
|
||||
|
||||
func Build(records []Record) (*Node, error) {
|
||||
panic("Please implement the Build function")
|
||||
}
|
351
go/tree-building/tree_building_test.go
Normal file
351
go/tree-building/tree_building_test.go
Normal file
@@ -0,0 +1,351 @@
|
||||
package tree
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Define a function Build(records []Record) (*Node, error)
|
||||
// where Record is a struct containing int fields ID and Parent
|
||||
// and Node is a struct containing int field ID and []*Node field Children.
|
||||
|
||||
var successTestCases = []struct {
|
||||
name string
|
||||
input []Record
|
||||
expected *Node
|
||||
}{
|
||||
{
|
||||
name: "empty input",
|
||||
input: []Record{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "one node",
|
||||
input: []Record{
|
||||
{ID: 0},
|
||||
},
|
||||
expected: &Node{
|
||||
ID: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "three nodes in order",
|
||||
input: []Record{
|
||||
{ID: 0},
|
||||
{ID: 1, Parent: 0},
|
||||
{ID: 2, Parent: 0},
|
||||
},
|
||||
expected: &Node{
|
||||
ID: 0,
|
||||
Children: []*Node{
|
||||
{ID: 1},
|
||||
{ID: 2},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "three nodes in reverse order",
|
||||
input: []Record{
|
||||
{ID: 2, Parent: 0},
|
||||
{ID: 1, Parent: 0},
|
||||
{ID: 0},
|
||||
},
|
||||
expected: &Node{
|
||||
ID: 0,
|
||||
Children: []*Node{
|
||||
{ID: 1},
|
||||
{ID: 2},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "three levels of nesting",
|
||||
input: []Record{
|
||||
{ID: 2, Parent: 1},
|
||||
{ID: 1, Parent: 0},
|
||||
{ID: 3, Parent: 2},
|
||||
{ID: 0},
|
||||
},
|
||||
expected: &Node{
|
||||
ID: 0,
|
||||
Children: []*Node{
|
||||
{
|
||||
ID: 1,
|
||||
Children: []*Node{
|
||||
{
|
||||
ID: 2,
|
||||
Children: []*Node{
|
||||
{ID: 3},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "more than two children",
|
||||
input: []Record{
|
||||
{ID: 3, Parent: 0},
|
||||
{ID: 2, Parent: 0},
|
||||
{ID: 1, Parent: 0},
|
||||
{ID: 0},
|
||||
},
|
||||
expected: &Node{
|
||||
ID: 0,
|
||||
Children: []*Node{
|
||||
{ID: 1},
|
||||
{ID: 2},
|
||||
{ID: 3},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "binary tree",
|
||||
input: []Record{
|
||||
{ID: 5, Parent: 1},
|
||||
{ID: 3, Parent: 2},
|
||||
{ID: 2, Parent: 0},
|
||||
{ID: 4, Parent: 1},
|
||||
{ID: 1, Parent: 0},
|
||||
{ID: 0},
|
||||
{ID: 6, Parent: 2},
|
||||
},
|
||||
expected: &Node{
|
||||
ID: 0,
|
||||
Children: []*Node{
|
||||
{
|
||||
ID: 1,
|
||||
Children: []*Node{
|
||||
{ID: 4},
|
||||
{ID: 5},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
Children: []*Node{
|
||||
{ID: 3},
|
||||
{ID: 6},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unbalanced tree",
|
||||
input: []Record{
|
||||
{ID: 5, Parent: 2},
|
||||
{ID: 3, Parent: 2},
|
||||
{ID: 2, Parent: 0},
|
||||
{ID: 4, Parent: 1},
|
||||
{ID: 1, Parent: 0},
|
||||
{ID: 0},
|
||||
{ID: 6, Parent: 2},
|
||||
},
|
||||
expected: &Node{
|
||||
ID: 0,
|
||||
Children: []*Node{
|
||||
{
|
||||
ID: 1,
|
||||
Children: []*Node{
|
||||
{ID: 4},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
Children: []*Node{
|
||||
{ID: 3},
|
||||
{ID: 5},
|
||||
{ID: 6},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var failureTestCases = []struct {
|
||||
name string
|
||||
input []Record
|
||||
}{
|
||||
{
|
||||
name: "root node has parent",
|
||||
input: []Record{
|
||||
{ID: 0, Parent: 1},
|
||||
{ID: 1, Parent: 0},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no root node",
|
||||
input: []Record{
|
||||
{ID: 1, Parent: 0},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "duplicate node",
|
||||
input: []Record{
|
||||
{ID: 0, Parent: 0},
|
||||
{ID: 1, Parent: 0},
|
||||
{ID: 1, Parent: 0},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "duplicate root",
|
||||
input: []Record{
|
||||
{ID: 0, Parent: 0},
|
||||
{ID: 0, Parent: 0},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "non-continuous",
|
||||
input: []Record{
|
||||
{ID: 2, Parent: 0},
|
||||
{ID: 4, Parent: 2},
|
||||
{ID: 1, Parent: 0},
|
||||
{ID: 0},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cycle directly",
|
||||
input: []Record{
|
||||
{ID: 5, Parent: 2},
|
||||
{ID: 3, Parent: 2},
|
||||
{ID: 2, Parent: 2},
|
||||
{ID: 4, Parent: 1},
|
||||
{ID: 1, Parent: 0},
|
||||
{ID: 0},
|
||||
{ID: 6, Parent: 3},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cycle indirectly",
|
||||
input: []Record{
|
||||
{ID: 5, Parent: 2},
|
||||
{ID: 3, Parent: 2},
|
||||
{ID: 2, Parent: 6},
|
||||
{ID: 4, Parent: 1},
|
||||
{ID: 1, Parent: 0},
|
||||
{ID: 0},
|
||||
{ID: 6, Parent: 3},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "higher id parent of lower id",
|
||||
input: []Record{
|
||||
{ID: 0},
|
||||
{ID: 2, Parent: 0},
|
||||
{ID: 1, Parent: 2},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func (n Node) String() string {
|
||||
return fmt.Sprintf("%d:%s", n.ID, n.Children)
|
||||
}
|
||||
|
||||
func TestMakeTreeSuccess(t *testing.T) {
|
||||
for _, tt := range successTestCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actual, err := Build(tt.input)
|
||||
if err != nil {
|
||||
var _ error = err
|
||||
t.Fatalf("Build for test case %q returned error %q. Error not expected.",
|
||||
tt.name, err)
|
||||
}
|
||||
if !reflect.DeepEqual(actual, tt.expected) {
|
||||
t.Fatalf("Build for test case %q returned %s but was expected to return %s.",
|
||||
tt.name, actual, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeTreeFailure(t *testing.T) {
|
||||
for _, tt := range failureTestCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actual, err := Build(tt.input)
|
||||
if err == nil {
|
||||
t.Fatalf("Build for test case %q returned %s but was expected to fail.",
|
||||
tt.name, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func shuffleRecords(records []Record) []Record {
|
||||
gen := rand.New(rand.NewSource(42))
|
||||
newRecords := make([]Record, len(records))
|
||||
for i, idx := range gen.Perm(len(records)) {
|
||||
newRecords[i] = records[idx]
|
||||
}
|
||||
return newRecords
|
||||
}
|
||||
|
||||
// Binary tree
|
||||
func makeTwoTreeRecords() []Record {
|
||||
records := make([]Record, 1<<16)
|
||||
for i := range records {
|
||||
if i == 0 {
|
||||
records[i] = Record{ID: 0}
|
||||
} else {
|
||||
records[i] = Record{ID: i, Parent: i >> 1}
|
||||
}
|
||||
}
|
||||
return shuffleRecords(records)
|
||||
}
|
||||
|
||||
var twoTreeRecords = makeTwoTreeRecords()
|
||||
|
||||
func BenchmarkTwoTree(b *testing.B) {
|
||||
if testing.Short() {
|
||||
b.Skip("skipping benchmark in short mode.")
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
Build(twoTreeRecords)
|
||||
}
|
||||
}
|
||||
|
||||
// Each node but the root node and leaf nodes has ten children.
|
||||
func makeTenTreeRecords() []Record {
|
||||
records := make([]Record, 10000)
|
||||
for i := range records {
|
||||
if i == 0 {
|
||||
records[i] = Record{ID: 0}
|
||||
} else {
|
||||
records[i] = Record{ID: i, Parent: i / 10}
|
||||
}
|
||||
}
|
||||
return shuffleRecords(records)
|
||||
}
|
||||
|
||||
var tenTreeRecords = makeTenTreeRecords()
|
||||
|
||||
func BenchmarkTenTree(b *testing.B) {
|
||||
if testing.Short() {
|
||||
b.Skip("skipping benchmark in short mode.")
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
Build(tenTreeRecords)
|
||||
}
|
||||
}
|
||||
|
||||
func makeShallowRecords() []Record {
|
||||
records := make([]Record, 10000)
|
||||
for i := range records {
|
||||
records[i] = Record{ID: i, Parent: 0}
|
||||
}
|
||||
return shuffleRecords(records)
|
||||
}
|
||||
|
||||
var shallowRecords = makeShallowRecords()
|
||||
|
||||
func BenchmarkShallowTree(b *testing.B) {
|
||||
if testing.Short() {
|
||||
b.Skip("skipping benchmark in short mode.")
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
Build(shallowRecords)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user