我正试图找出在Go中递归浏览[string]int
地图的最佳方法。我正在构建一个涉及多个国家的游戏,并最终由两个团队组成。
目标是将具有最低“得分”的前两个国家匹配到其自己的两个组中,并将其添加回集合,为新地图提供这些国家/地区得分的总值。
然后递归地对所有组执行此操作,最后以一个组和一个总值结束。
例如,如果你有:
score := map[string]int{
"Canada": 7,
"US": 2,
"Germany": 3,
"Korea": 4,
}
group1 = {[US:2] [Germany:3]}
,总共5
group1现在将被放回到初始集合中,其得分为5,因为它取得了两个最低分数。我们现在有:
score := map[string]int{
"Canada": 7,
"Korea": 4,
group1: `US:2 Germany:3` with a total of 5
}
如果这是集合中的最低分,那么下一次迭代将是:
group2 = {[Korea:4] [group1:5]}
score := map[string]int{
"Canada": 7,
group2: `Korea:4 group1:5` with a total of 9
}
依此类推,直到你留下一个......我认为基本结构应该是这样的。但是,我不确定这样做的正确方法,因为数据结构现在包含[string]int
地图以及这个新地图。
我意识到这不是一个通用的问题,但可以使用接口吗?我是Go的新手,所以建议会有所帮助。
这是一个进一步说明我的意思的例子: https://play.golang.org/p/cnkTc0HBY4
答案 0 :(得分:2)
你的问题可以很容易"使用heap data structure解决。
package main
import (
"container/heap"
"fmt"
)
// Something that has a score
type Scoreable interface {
fmt.Stringer
Score() int
}
// A country has a name and a score
type Country struct {
name string
score int
}
// Country implements Scoreable
func (c Country) Score() int {
return c.score
}
// ... and fmt.Stringer
func (c Country) String() string {
return fmt.Sprintf("%s [%d]", c.name, c.score)
}
// A team consists of two Scoreable's and has itself a score
type Team struct {
team1, team2 Scoreable
score int
}
// Team implements Scoreable
func (t Team) Score() int {
return t.score
}
// ... and fmt.Stringer
func (t Team) String() string {
return fmt.Sprintf("(%s + %s)", t.team1.String(), t.team2.String())
}
// The heap will be implemented using a slice of Scoreables
type TeamHeap []Scoreable
// TeamHeap implements heap.Interface
func (th TeamHeap) Len() int {
return len(th)
}
func (th TeamHeap) Less(i, j int) bool {
return th[i].Score() < th[j].Score()
}
func (th TeamHeap) Swap(i, j int) {
th[i], th[j] = th[j], th[i]
}
func (th *TeamHeap) Push(t interface{}) {
*th = append(*th, t.(Scoreable))
}
func (th *TeamHeap) Pop() interface{} {
old := *th
n := len(old)
t := old[n-1]
*th = old[0 : n-1]
return t
}
// The main function
func main() {
// Create a heap and initialize it
teams := &TeamHeap{}
heap.Init(teams)
// Push the countries (NB: heap.Push(), not teams.Push())
heap.Push(teams, Country{"Canada", 7})
heap.Push(teams, Country{"US", 2})
heap.Push(teams, Country{"Germany", 3})
heap.Push(teams, Country{"Korea", 4})
// Take the two teams with lowest score and make a new team of them
// Repeat this until there's only one team left
for teams.Len() > 1 {
t1 := heap.Pop(teams).(Scoreable)
t2 := heap.Pop(teams).(Scoreable)
heap.Push(teams, Team{t1, t2, t1.Score() + t2.Score()})
}
// Print the teams that we now have in the heap
for teams.Len() > 0 {
t := heap.Pop(teams).(Team)
fmt.Println(t)
}
}
您可以在Go Playground找到runnable code。
答案 1 :(得分:2)
package main
import (
"container/heap"
"fmt"
)
//Recursive data structure may looks something like
type Group struct {
Score int
Left *Group
Right *Group
Country string
}
//You can use slice to hold them organized in tree
type GrHeap []Group
//To implement your logic you can use stdlib/container/heap Heap interface
//you must implement Heap interface for your slice
func (h GrHeap) Len() int { return len(h) }
func (h GrHeap) Less(i, j int) bool { return h[i].Score < h[j].Score }
func (h GrHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *GrHeap) Push(x interface{}) {
// Push and Pop use pointer receivers because they modify the slice's length,
// not just its contents.
*h = append(*h, x.(Group))
}
func (h *GrHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
func main() {
//you most likely already have a map
//anyway it will be handy to keep it for convenient access to individual country
score := map[string]int{
"Canada": 7,
"US": 2,
"Germany": 3,
"Korea": 4,
}
//here we allocate heap
gr := make(GrHeap, 0)
//populate it from map
for k, v := range score {
g := Group{v, nil, nil, k}
gr = append(gr, g)
}
//and initialize
heap.Init(&gr)
//and here we use heap magic to implement your logic
for len(gr) > 2 {
l := heap.Pop(&gr).(Group)
r := heap.Pop(&gr).(Group)
ng := Group{l.Score + r.Score, &l, &r, ""}
heap.Push(&gr, ng)
}
fmt.Println(gr)
fmt.Println(gr[1].Left)
fmt.Println(gr[1].Right.Left)
//and you can see it works https://play.golang.org/p/gugJxJb7rr
}
答案 2 :(得分:0)
您可以使用map[string]interface{}
尝试Type assertion
。
这是演示
package main
import "fmt"
const total = "total"
func GetValue(i interface{}) int {
value, ok := i.(int)
if ok {
return value
}
return i.(map[string]interface{})[total].(int)
}
func main() {
score := map[string]interface{}{
"Canada": 7,
"US": 2,
"Germany": 3,
"Korea": 4,
}
groupCount := 0
for len(score) > 2 {
var (
firstMin = math.MaxInt32
secondMin = math.MaxInt32
firstKey = ""
secondKey = ""
)
for k, v := range score {
iv := GetValue(v)
if iv < firstMin {
secondMin = firstMin
secondKey = firstKey
firstMin = iv
firstKey = k
continue
}
if iv < secondMin {
secondMin = iv
secondKey = k
continue
}
}
groupCount++
score[fmt.Sprintf("Group%d", groupCount)] = map[string]interface{}{
firstKey: score[firstKey],
secondKey: score[secondKey],
total: GetValue(score[firstKey])+ GetValue(score[secondKey]),
}
delete(score, firstKey)
delete(score, secondKey)
}
fmt.Println(score)
}