如何在golang中获得两个切片的交叉点?

zxlwwiss  于 2022-12-07  发布在  Go
关注(0)|答案(7)|浏览(183)

在Go语言中,有没有有效的方法来求两个切片的交集?
我想避免嵌套的for循环像解

slice1 := []string{"foo", "bar","hello"}
slice2 := []string{"foo", "bar"}

intersection(slice1, slice2)
=> ["foo", "bar"]

一样字符串的顺序无关紧要

h79rfbju

h79rfbju1#

How do I get the intersection between two arrays as a new array?

  • 简单交集:将A中的每个元素与B中的每个元素进行比较(O(n^2)
  • 散列交集:将它们放入哈希表(O(n)
  • 排序交集:排序A并执行优化交集(O(n*log(n))

所有这些都在这里实现
https://github.com/juliangruber/go-intersect

iqxoj9l9

iqxoj9l92#

这是两切片求交的最佳方法,时间复杂度太低。
时间复杂性:时间复杂度O(m+n)
m =第一切片长度。
n =第二切片长度。

func intersection(s1, s2 []string) (inter []string) {
    hash := make(map[string]bool)
    for _, e := range s1 {
        hash[e] = true
    }
    for _, e := range s2 {
        // If elements present in the hashmap then append intersection list.
        if hash[e] {
            inter = append(inter, e)
        }
    }
    //Remove dups from slice.
    inter = removeDups(inter)
    return
}

//Remove dups from slice.
func removeDups(elements []string)(nodups []string) {
    encountered := make(map[string]bool)
    for _, element := range elements {
        if !encountered[element] {
            nodups = append(nodups, element)
            encountered[element] = true
        }
    }
    return
}
ndasle7k

ndasle7k3#

简单、通用和多切片!(Go 1.18)
时间复杂性:可以是线性的

func interSection[T constraints.Ordered](pS ...[]T) (result []T) {
    hash := make(map[T]*int) // value, counter
    for _, slice := range pS {
        duplicationHash := make(map[T]bool) // duplication checking for slice
        for _, value := range slice {
            if _, ok := duplicationHash[value]; !ok {
                if counter := hash[value]; counter != nil {
                    *counter++
                } else {
                    i := 1
                    hash[value] = &i
                }
                duplicationHash[value] = true
            }
        }
    }
    result = make([]T, 0)
    for value, counter := range hash {
        if *counter >= len(pS) {
            result = append(result, value)
        }
    }
    return
}
func main() {
    slice1 := []string{"foo", "bar", "hello"}
    slice2 := []string{"foo", "bar"}
    fmt.Println(interSection(slice1, slice2))
    // [foo bar]

    ints1 := []int{1, 2, 3, 9, 8}
    ints2 := []int{10, 4, 2, 4, 8, 9} // have duplicated values
    ints3 := []int{2, 4, 8, 1}
    fmt.Println(interSection(ints1, ints2, ints3))
    // [2 8]
}

Playground:https://go.dev/play/p/l5-4PkEw2xp

zaqlnxep

zaqlnxep4#

如果你的[]string中没有空格,也许你需要这段简单的代码:

func filter(src []string) (res []string) {
    for _, s := range src {
        newStr := strings.Join(res, " ")
        if !strings.Contains(newStr, s) {
            res = append(res, s)
        }
    }
    return
}

func intersections(section1, section2 []string) (intersection []string) {
    str1 := strings.Join(filter(section1), " ")
    for _, s := range filter(section2) {
        if strings.Contains(str1, s) {
            intersection = append(intersection, s)
        }
    }
    return
}
yfjy0ee7

yfjy0ee75#

试试看
https://go.dev/play/p/eGGcyIlZD6y

first := []string{"one", "two", "three", "four"}
second := []string{"two", "four"}

result := intersection(first, second) // or intersection(second, first)

func intersection(first, second []string) []string {
    out := []string{}
    bucket := map[string]bool{}

    for _, i := range first {
        for _, j := range second {
            if i == j && !bucket[i] {
                out = append(out, i)
                bucket[i] = true
            }
        }
    }

    return out
}
wmtdaxz3

wmtdaxz36#

https://github.com/viant/toolbox/blob/a46fd679bbc5d07294b1d1b646aeacd44e2c7d50/collections.go#L869-L920
另一个时间复杂度为O(m+n)的解决方案,它使用哈希Map。它与这里讨论的其他解决方案相比有两个不同之处。

  • 将目标切片作为参数传递,而不是返回新切片
  • 更快地用于常用类型(如string/int),而不是用于所有类型
nvbavucw

nvbavucw7#

是的,有几种不同的方法来实现它。这里有一个可以优化的例子。

package main

import "fmt"

func intersection(a []string, b []string) (inter []string) {
    // interacting on the smallest list first can potentailly be faster...but not by much, worse case is the same
    low, high := a, b
    if len(a) > len(b) {
        low = b
        high = a
    }

    done := false
    for i, l := range low {
        for j, h := range high {
            // get future index values
            f1 := i + 1
            f2 := j + 1
            if l == h {
                inter = append(inter, h)
                if f1 < len(low) && f2 < len(high) {
                    // if the future values aren't the same then that's the end of the intersection
                    if low[f1] != high[f2] {
                        done = true
                    }
                }
                // we don't want to interate on the entire list everytime, so remove the parts we already looped on will make it faster each pass
                high = high[:j+copy(high[j:], high[j+1:])]
                break
            }
        }
        // nothing in the future so we are done
        if done {
            break
        }
    }
    return
}

func main() {
    slice1 := []string{"foo", "bar", "hello", "bar"}
    slice2 := []string{"foo", "bar"}
    fmt.Printf("%+v\n", intersection(slice1, slice2))
}

现在,上面定义的交集方法将只对stringsslices进行操作,就像您的示例一样。理论上,您可以创建类似于func intersection(a []interface, b []interface) (inter []interface)的定义,但是您将依赖于反射和类型转换,以便可以进行比较,这将增加延迟并使代码更难阅读。为您关心的每种类型编写单独的函数可能更容易维护和阅读。
func intersectionString(a []string, b []string) (inter []string)
func intersectionInt(a []int, b []int) (inter []int)
func intersectionFloat64(a []Float64, b []Float64) (inter []Float64)
然后,您可以创建自己的包,并在确定要如何实现它后重用它。

package intersection

func String(a []string, b []string) (inter []string)

func Int(a []int, b []int) (inter []int)

func Float64(a []Float64, b []Float64) (inter []Float64)

相关问题