继续刷LeetCode,做做脑力运动,今天继续使用go来做开发,随机刷了一道编号为990的题:satisfiability of equality equations,中文名叫等式方程的可满足性,难度:中等。
难度确实高了一些,需要的解题步骤复杂了不少,开始拆解错了问题,耽搁了一些功夫,整体来说go语言的熟悉程度影响最大,go官方没有set以及初始化复杂结构的方式也不是好理解,这个后续得理理,或许需要看些源码学习一下了。
官方一共181个case,有几个就是为了测试性能的,先放一个最终结果吧
执行用时 : 28 ms, 在所有 Go 提交中击败了 76.19% 的用户 内存消耗 : 6.1 MB, 在所有 Go 提交中击败了 100.00% 的用户
详细信息参考这里 等式方程的可满足性 - 提交记录 - 力扣 (LeetCode) 。
简单说一下解题思路,整个过程分为两个阶段:
- 首先把输入的信息转化为方便遍历的结构allData,针对每一个等式,记录该变量的等价元素集合以及不等价元素集合,这里有以下注意点
- 在go中没有找到顺手的集合,用map代替
- 如果遇到
a==a直接跳过 - 如果遇到
a!=a的情况,可以直接返回处理 - 这里要主要双向处理,即所有关系是对等
- 因为遇到一个语法障碍,导致初始化部分的代码非常别扭,后面再仔细说
- 然后,遍历整个allData,看看检查其中每个元素是否满足所有的等式方程,这里的方法也比较简单粗暴,把不想等集合中每个元素看看是否也存在相等集合中,如果存在,则返回不满足,这里只有一个注意点:
- 因为等式的传递性,所以要递归检查所有等式集合中的下一级,这个递归只需要注意递归跳出条件就好
最后通过测试的代码如下。
// Relation for equal and no-equal
type Relation struct {
eq map[byte]bool
ne map[byte]bool
}
func equationsPossible(equations []string) bool {
fmt.Println(equations)
var allData = make(map[byte]Relation)
for _, equation := range equations {
e1 := equation[0]
e2 := equation[3]
if equation[1] == '=' {
if e1 == e2 {
continue
}
a := Relation{}
a.eq = map[byte]bool{e2: true}
a.ne = allData[e1].ne
for k := range allData[e1].eq {
a.eq[k] = true
}
allData[e1] = a
b := Relation{}
b.eq = map[byte]bool{e1: true}
for k := range allData[e2].eq {
b.eq[k] = true
}
b.ne = allData[e2].ne
allData[e2] = b
} else {
if e1 == e2 {
return false
}
a := Relation{}
a.ne = map[byte]bool{e2: true}
a.eq = allData[e1].eq
for k := range allData[e1].ne {
a.ne[k] = true
}
allData[e1] = a
b := Relation{}
b.ne = map[byte]bool{e1: true}
for k := range allData[e2].ne {
b.ne[k] = true
}
b.eq = allData[e2].eq
allData[e2] = b
}
}
// fmt.Println(allData)
for e1, v := range allData {
for e2 := range v.ne {
if checkFailed(e1, e2, allData, map[byte]bool{}) {
return false
}
if checkFailed(e2, e1, allData, map[byte]bool{}) {
return false
}
}
}
return true
}
func checkFailed(e1 byte, e2 byte, allData map[byte]Relation, path map[byte]bool) bool {
if _, exists := path[e1]; exists {
return false
}
if _, exists := allData[e1].eq[e2]; exists {
return true
}
for k := range allData[e1].eq {
path[e1] = true
if checkFailed(k, e2, allData, path) {
return true
}
}
return false
}
还有测试用例,大部分取自官方测试用例集合,包含大多数典型情况。
package main
import "testing"
func Test_rob(t *testing.T) {
type args struct {
nums []int
}
tests := []struct {
name string
args args
want int
}{
{"TestCase 1", args{[]string{"a==b"}}, true},
{"TestCase 2", args{[]string{"a==b", "b!=a"}}, false},
{"TestCase 3", args{[]string{"b==a", "a==b"}}, true},
{"TestCase 4", args{[]string{"a==b", "b==c", "a==c"}}, true},
{"TestCase 5", args{[]string{"a==b", "b!=c", "c==a"}}, false},
{"TestCase 6", args{[]string{"c==c", "b==d", "x!=z"}}, true},
{"TestCase 7", args{[]string{"a!=b", "b!=c", "c!=a"}}, true},
{"TestCase 8", args{[]string{"a==a"}}, true},
{"TestCase 9", args{[]string{"a!=a"}}, false},
{"TestCase 9", args{[]string{"a==b", "e==c", "b==c", "a!=e"}}, false},
{"TestCase 10", args{[]string{"b==b", "b==e", "e==c", "d!=e"}}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := rob(tt.args.nums); got != tt.want {
t.Errorf("rob() = %v, want %v", got, tt.want)
}
})
}
}
收获
先记录一下遇到的初始化复杂数据结构的问题,如果像下面这些代码这么写,编译器会报错cannot assign to struct field allData['a'].eq in mapgo
type Relation struct {
eq map[byte]bool
ne map[byte]bool
}
var allData = make(map[byte]Relation)
allData['a'] = Relation{}
allData['a'].eq = make(map[byte]bool)
如果写成下面在我看来等价的形式就没问题,直觉上很奇怪
type Relation struct {
eq map[byte]bool
ne map[byte]bool
}
var allData = make(map[byte]Relation)
a := Relation{}
a.eq = map[byte]bool{e2: true}
allData['a'] = a
或者下面的写法也可以
allData[e1] = Relation{map[byte]bool{}, map[byte]bool{}}
allData[e1].eq['b'] = true
看了些关于这个错误的解释、Go maps in action - The Go Blog 、github上的issueproposal: spec: cannot assign to a field of a map element directly: m["foo"].f = x · Issue #3117 · golang/go 以及语言规范中关于赋值部分,模糊有点感觉大体能理解,但是感觉是没处理好的一个地方。
在go中map是个指针类型,在这种情况下不能直接赋值,下面这个例子更清晰,貌似这种写法更好一些
allData[e1] = Relation{map[byte]bool{}, map[byte]bool{}}
if thisData, ok := allData[e1]; ok {
thisData.eq = map[byte]bool{}
allData[e1] = thisData
} else {
// 初始化该元素
}
看来后面得多看看源码,熟悉一下编码规范了。
Reference
- CodeReviewComments · golang/go Wiki
- Go maps in action - The Go Blog
- proposal: spec: cannot assign to a field of a map element directly: m["foo"].f = x · Issue #3117 · golang/go
- The Go Programming Language Specification - The Go Programming Language
- The Go Programming Language Specification - The Go Programming Language
- go - Why do I get a "cannot assign" error when setting value to a struct as a value in a map? - Stack Overflow