我有一个字符串数组,我需要将此字符串与另一个字符串数组进行比较,但是它们的顺序可能不同。比较两个数组的最佳方法是什么?
到目前为止,这是我所想的,只是想知道我是否缺少一种更简单/更有效的方法。
func unorderedEqual(first, second []string) bool {
if len(first) != len(second) {
return false
}
for _, value := range first {
if !contains(value, second) {
return false
}
}
return true
}
func contains(needle string, haystack []string) bool {
for _, matchValue := range haystack {
if matchValue == needle {
return true
}
}
return false
}
答案 0 :(得分:4)
鉴于您正在进行长度检查,我将假设它们是1:1,只是顺序不同。
您可以使用map[string]bool
通过一次检查(每次检查)来检查两者是否存在。这利用了以下事实:当不存在密钥时,map
返回bool
的零值,即false
。
免责声明:从技术上讲,这是订单O(n)* O(map)。 Go Programming Language Specification对地图类型不做任何性能保证。
https://play.golang.org/p/2LUjN5LkXLL
func unorderedEqual(first, second []string) bool {
if len(first) != len(second) {
return false
}
exists := make(map[string]bool)
for _, value := range first {
exists[value] = true
}
for _, value := range second {
if !exists[value] {
return false
}
}
return true
}
如果您想对内存使用情况一窍不通,可以使用bool
(一堆map[string]struct{}
(通常可以忽略,但每个都可以忽略)来保存自己(空结构),您只需按照本示例中的方式检查是否存在。
https://play.golang.org/p/MjwP_wCtYZV
设置
exists[value] = struct{}{}
检查
if _, ok := exists[value]; !ok {
return false
}
答案 1 :(得分:1)
理想情况下,这应该是注释,但 TLDR 是使用 Husain 的排序和比较更正确和更快。
详情。
对于任何查看上面 RayfenWindspear 答案的人(目前排名最高),乍一看它看起来不错,但请注意,它不会检查完全相等性,而只是检查第二个列表中的每个元素都在第一个列表中.反过来也需要为真,但不通过此方法检查。因此它无法通过此测试(重复 bb
)。:
assert.False(t, unorderedEqual([]string{"aa", "cc", "bb"}, []string{"aa", "bb", "bb"}))
当然你可以只运行两次就得到正确的结果,这只是一个线性因素
func DoubleUnorderedEqual(a, b []string) bool {
return unorderedEqual(a, b) && unorderedEqual(b, a)
}
Husain 提出的先排序再检查的建议应该排名更高,因为它是正确的,而且对于更大的列表也更快。
这是 Husain 在导出函数中的代码:
func SortCompare(a, b []string) bool {
if len(a) != len(b) {
return false
}
sort.Strings(a)
sort.Strings(b)
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
你可以在上面运行一些测试(它通过)
func TestSortCompare(t *testing.T) {
assert.True(t, SortCompare([]string{"aa"}, []string{"aa"}))
assert.False(t, SortCompare([]string{"aa"}, []string{"bb"}))
assert.False(t, SortCompare([]string{"aa"}, []string{"bb", "cc"}))
assert.True(t, SortCompare([]string{"cc", "bb"}, []string{"bb", "cc"}))
assert.True(t, SortCompare([]string{"aa", "cc", "bb"}, []string{"aa", "bb", "cc"}))
assert.False(t, SortCompare([]string{"aa", "cc", "bb"}, []string{"aa", "bb", "bb"}))
}
这是一些示例基准测试....
func getStrings() ([]string, []string) {
a := []string{"a", "foo", "bar", "ping", "pong"}
b := []string{"pong", "foo", "a", "bar", "ping"}
return a, b
}
func BenchmarkSortCompare(b *testing.B) {
s0, s1 := getStrings()
var outcome bool
for n := 0; n < b.N; n++ {
outcome = SortCompare(s0, s1)
}
fmt.Println(outcome)
}
func BenchmarkDoubleUnorderedEqual(b *testing.B) {
s0, s1 := getStrings()
var outcome bool
for n := 0; n < b.N; n++ {
outcome = DoubleUnorderedEqual(s0, s1)
}
fmt.Println(outcome)
}
结果......
BenchmarkSortCompare-32 2637952 498 ns/op
BenchmarkDoubleUnorderedEqual-32 3060261 381 ns/op
所以在这么小的尺寸下运行两次 map 方法会稍微快一些……但是多加几个字符串,sort 方法胜出 10 倍。我没有考虑混乱程度的影响在字符串中,但它们足够无序,乍一看并不是一个明显不公平的测试。
func getStrings2() ([]string, []string) {
a := []string{"a", "foo", "bar", "ping", "pong", "b", "c", "g", "e", "f", "d", "h", "i", "j", "q", "l", "n", "o", "p", "k", "r", "s", "t", "u", "v", "w", "x", "y", "z"}
b := []string{"pong", "foo", "a", "bar", "ping", "p", "r", "q", "s", "u", "t", "v", "j", "x", "y", "z", "b", "e", "d", "c", "h", "g", "f", "i", "w", "k", "l", "n", "o"}
return a, b
}
func BenchmarkSortCompare2(b *testing.B) {
s0, s1 := getStrings2()
var outcome bool
for n := 0; n < b.N; n++ {
outcome = SortCompare(s0, s1)
}
fmt.Println(outcome)
}
func BenchmarkDoubleUnorderedEqual2(b *testing.B) {
s0, s1 := getStrings2()
var outcome bool
for n := 0; n < b.N; n++ {
outcome = DoubleUnorderedEqual(s0, s1)
}
fmt.Println(outcome)
}
结果:
BenchmarkSortCompare2-32 454641 2797 ns/op
BenchmarkDoubleUnorderedEqual2-32 44420 26714 ns/op
结论 - 我将使用 Husain 的排序和比较......
答案 2 :(得分:0)
使用this专用软件包,您将不得不使用[]interface{}
而不是[]string
,然后照这样
package main
import (
"fmt"
"github.com/deckarep/golang-set"
)
func main() {
some := []interface{}{"a", "b", "c"}
other := []interface{}{"a", "b", "d"}
fmt.Println(
mapset.NewThreadUnsafeSetFromSlice(some).
Difference(mapset.NewThreadUnsafeSetFromSlice(other)).Cardinality() == 0,
)
// Output: false
}
答案 3 :(得分:0)
与语言无关的通用语言:
我不了解GO,但是您似乎天真地从B中的A中搜索每个元素。在最坏的情况下,您会在B上获得许多迭代。使用高性能算法进行排序似乎效率更高,即使附加操作。
很不幸,我不提供代码示例,因为我不了解GO。
答案 4 :(得分:0)
您可以先对数组进行排序,然后再按索引进行检查:
package main
import (
"fmt"
"sort"
)
func main() {
s1 := []string{"first", "second", "third"}
s2 := []string{"third", "first", "second"}
if len(s1) != len(s2) {
fmt.Println("not equal")
}
sort.Strings(s1)
sort.Strings(s2)
for i := 0; i < len(s1); i++ {
if s1[i] != s2[i] {
fmt.Println("not equal")
return
}
}
fmt.Println("equal")
}
或在playground中。
排序的思想是使比较更加容易和快捷。排序然后按索引进行比较是O(n log n),而2循环检查则需要O(n ^ 2)。
答案 5 :(得分:0)
不幸的是,我无法发表评论,因为我落后于所需的声誉(更多是读者;-))。
但我想表明 @RayfenWindspear 的答案是错误的!他的方法只是检查 second
中的元素是否存在于 first
中,反之亦然:
这是关于 Playground 的证明(我拿走了他的片段,只是改变了切片的值)
我认为 @Husain 的解决方案很好。