2 回答
TA贡献1810条经验 获得超5个赞
sort包演示了如何使用接口以与类型无关的方式实现算法。
线性搜索需要两个基本操作,具体取决于 haystack 元素类型:Len 和 Equal。因此,我们可以编写以下 Haystack 接口和使用它的搜索函数:
type Haystack interface {
Len() int
Equal(int, interface{}) bool
}
func Search(haystack Haystack, needle interface{}) int {
for i := 0; i < haystack.Len(); i++ {
if haystack.Equal(i, needle) {
return i
}
}
return -1
}
这使得 Haystack 的编写实现变得简单,但不是类型安全的:
type Strings []string
func (s Strings) Len() int { return len(s) }
func (s Strings) Equal(i int, x interface{}) bool { return s[i] == x.(string) }
type Ints []int
func (s Ints) Len() int { return len(s) }
func (s Ints) Equal(i int, x interface{}) bool { return s[i] == x.(int) }
func main() {
strings := []string{"b", "a", "c", "d"}
fmt.Println(Search(Strings(strings), "c")) // 2
fmt.Println(Search(Strings(strings), "e")) // -1
ints := []int{2, 1, 3, 4}
fmt.Println(Search(Ints(ints), 3)) // 2
fmt.Println(Search(Ints(ints), 5)) // -1
}
请注意 Equal 方法中的类型断言。为了使这个类型安全,我们必须去掉interface{}Equal 的参数:
type Haystack interface {
Len() int
Equal(int) bool
}
func Search(haystack Haystack) int {
for i := 0; i < haystack.Len(); i++ {
if haystack.Equal(i) {
return i
}
}
return -1
}
type Strings struct {
hs []string
needle string
}
func (s Strings) Len() int { return len(s.hs) }
func (s Strings) Equal(i int) bool { return s.hs[i] == s.needle }
type Ints struct {
hs []int
needle int
}
func (s Ints) Len() int { return len(s.hs) }
func (s Ints) Equal(i int) bool { return s.hs[i] == s.needle }
func main() {
strings := []string{"b", "a", "c", "d"}
fmt.Println(Search(Strings{strings, "c"})) // 2
fmt.Println(Search(Strings{strings, "e"})) // -1
ints := []int{2, 1, 3, 4}
fmt.Println(Search(Ints{ints, 3})) // 2
fmt.Println(Search(Ints{ints, 5})) // -1
}
这使得界面实现和搜索功能的使用变得更加复杂。
这个故事的寓意是,以这种方式使用接口需要足够复杂的算法才值得这样做。如果为特定类型编写接口实现比为算法编写具体实现需要更多工作,那么只需编写您需要的具体函数即可:
func SearchStr(haystack []string, needle string) int {
for i, x := range haystack {
if x == needle {
return i
}
}
return -1
}
func SearchInt(haystack []int, needle int) int {
for i, x := range haystack {
if x == needle {
return i
}
}
return -1
}
func main() {
strings := []string{"b", "a", "c", "d"}
fmt.Println(SearchStr(strings, "c")) // 2
fmt.Println(SearchStr(strings, "e")) // -1
ints := []int{2, 1, 3, 4}
fmt.Println(SearchInt(ints, 3)) // 2
fmt.Println(SearchInt(ints, 5)) // -1
}
TA贡献1802条经验 获得超10个赞
目前,不可能构建一个符合您所有标准的解决方案。一旦实现泛型,这将成为可能。或者您可以尝试使用 构建一个reflect,但这会产生一个复杂且可能缓慢的解决方案......所以我通常建议不要使用reflect像这样简单的东西(请参见下面的第二个片段)。
你现在可以做的是使用类似的东西:
func FindFirst(n int, f func(int) bool) int {
for i := 0; i < n; i++ {
if f(i) {
return i
}
}
return -1
}
// in your code (s is the slice, e the value you are searching for)
i := FindFirst(len(s), func(i int) bool {
return s[i] == e
})
if i != -1 {
// i is the index of the element with value e
}
正如你可以想象的那样,这没有多大意义......因为简单地显式地写出循环可以说更简单、更快、更惯用:
// in your code (s is the slice, e the value you are searching for)
for i, v := range s {
if v == e {
_ = i // i is the index of the element with value e
break
}
}
显然,只有当切片中的元素数量很少时,整个方法(线性扫描)才合理。sort.Slice如果您的切片很大并且很少更改,那么(从时间复杂度的角度来看)首先对其进行排序 ( ),然后sort.Search对排序后的切片进行二分搜索 ( )可能更有意义。或者,您也可以使用 amap来代替: 在这种情况下(假设键很小)查找的时间复杂度为 O(1)。
- 2 回答
- 0 关注
- 131 浏览
添加回答
举报