Go 中 slice 的 In 功能实现探索

2020-01-28 14:11:28王冬梅


func InIntSliceSortedFunc(haystack []int) func(int) bool {
 sort.Ints(haystack)

 return func(needle int) bool {
 index := sort.SearchInts(haystack, needle)
 return index < len(haystack) && haystack[index] == needle
 }
}

上面的实现,我们通过调用 InIntSliceSortedFunc 对 haystack 切片排序,并返回一个可多次使用的函数。

使用案例如下:


in := gotin.InIntSliceSortedFunc(haystack)

for i := 0; i<maxNeedle; i++ {
 if in(i) {
 fmt.Printf("%d is in %v", i, haystack)
 }
}

二分查找的方式有什么不足呢?

我想到的重要一点,要实现二分查找,元素必须是可排序的,如 int,string,float 类型。而对于结构体、切片、数组、映射等类型,使用起来就不是那么方便,当然,如果要用,也是可以的,不过需要我们进行一些适当扩展,按指定标准排序,比如结构的某个成员。

到此,二分查找的 in 实现就介绍完毕了。

map key

本节介绍 map key 方式。它的算法复杂度是 O1,无论数据量多大,查询性能始终不变。它主要依赖的是 Go 中的 map 数据类型,通过 hash map 直接检查 key 是否存在,算法大家应该都比较熟悉,通过 key 可直接映射到索引位置。

我们常会用到这个方法。


_, ok := m[k]
if ok {
 fmt.Println("Found")
}

那么它和 in 如何结合呢?一个案例就说明白了这个问题。

假设,我们有一个 []int 类型变量,如下:

s := []int{1, 2, 3}

为了使用 map 的能力检查某个元素是否存在,可以将 s 转化 map[int]struct{}。


m := map[interface{}]struct{}{
 1: struct{}{},
 2: struct{}{},
 3: struct{}{},
 4: struct{}{},
}

如果检查某个元素是否存在,只需要通过如下写法即可确定:


k := 4
if _, ok := m[k]; ok {
 fmt.Printf("%d is foundn", k)
}

是不是非常简单?

补充一点,关于这里为什么使用 struct{},可以阅读我之前写的一篇关于Go 中如何使用 set 的文章。

按照这个思路,实现函数如下:


func MapKeyInIntSlice(haystack []int, needle int) bool {
 set := make(map[int]struct{})

 for _ , e := range haystack {
 set[e] = struct{}{}
 }

 _, ok := set[needle]
 return ok
}

实现起来不难,但和二分查找有着同样的问题,开始要做数据处理,将 slice 转化为 map。如果是每次数据相同,稍微修改下它的实现。


func InIntSliceMapKeyFunc(haystack []int) func(int) bool {
 set := make(map[int]struct{})

 for _ , e := range haystack {
 set[e] = struct{}{}
 }

 return func(needle int) bool {
 _, ok := set[needle]
 return ok
 }
}

对于相同的数据,它会返回一个可多次使用的 in 函数,一个使用案例如下: