1. 单个数组/切片去重
合并两个整型切片,返回没有重复元素的切片,有两种去重策略
1.1 通过双重循环来过滤重复元素(时间换空间)
// 通过两重循环过滤重复元素
func RemoveRepByLoop(slc []int) []int {
result := []int{} // 存放结果
for i := range slc{
flag := true
for j := range result{
if slc[i] == result[j] {
flag = false // 存在重复元素,标识为false
break
}
}
if flag { // 标识为false,不添加进结果
result = append(result, slc[i])
}
}
return result
}
1.2. 通过字典来过滤(空间换时间)
// 通过map主键唯一的特性过滤重复元素
func RemoveRepByMap(slc []int) []int {
result := []int{}
tempMap := map[int]byte{} // 存放不重复主键
for _, e := range slc{
l := len(tempMap)
tempMap[e] = 0
if len(tempMap) != l{ // 加入map后,map长度变化,则元素不重复
result = append(result, e)
}
}
return result
}
ps : 这里为了节省内存,使用map[int]byte。 因为map的value并没有用到,所以什么类型都可以。
效率第一,如果节省计算时间,则可以采用如下方式
// 元素去重
func RemoveRep(slc []int) []int{
if len(slc) < 1024 {
// 切片长度小于1024的时候,循环来过滤
return RemoveRepByLoop(slc)
}else{
// 大于的时候,通过map来过滤
return RemoveRepByMap(slc)
}
}
ps: 1024 这个数字不是特别精准,我是使用go test 的基准测试,手工的比较的。大约在这个数量超上,使用map方式的速度要快,小于这个数量级后,loop方式要快,而且省内存。
2.查找两个数组的异同
最近项目上碰到个小需求,输入是两个数组,一个旧数组一个新数组,要求获取新数组相对旧数组所有新增和删除的元素,例如:
输入:
arr_old: {"1", "2", "4", "5", "7", "9"}
arr_new: {"2", "3", "4", "6", "7"}
返回:
arr_added: {"3", "6"}
arr_deleted: {"1", "5", "9"}
go的标准库中没有类似的直接比较的方法,需要自己具体实现,最简单的方法当然是旧数组的每个元素去新数组,找不到的就是删除的,然后新数组的元素再挨个去旧数组找一遍,找不到就是新增的,但这个方法效率实在太低了。
这里我使用了一种基于集合运算的思想,即分别求两个数组的交集和并集,并集减去交集就是所有发生变化的元素(要么是新增的,要么是删除的),遍历这个集合中的元素去旧数组中查找,如果在旧数组中找到,那么就是删除掉的元素;反之,如果找不到,则一定能在新数组中找到(用不着真的再去遍历一次),那么就是新增的元素。
上代码,这里有个技巧,就是利用go中map键唯一性的特性,用数组的元素作为map的key,通过map来实现快速查找。
package main
import (
"fmt"
)
func main() {
src := []string{"1", "2", "4", "5", "7", "9"}
dest := []string{"2", "3", "4", "6", "7"}
added, removed := Arrcmp(src, dest)
fmt.Printf("add: %v\\nrem: %v\\n", added, removed)
}
func Arrcmp(src []string, dest []string) ([]string, []string) {
msrc := make(map[string]byte) //按源数组建索引
mall := make(map[string]byte) //源+目所有元素建索引
var set []string //交集
//1.源数组建立map
for _, v := range src {
msrc[v] = 0
mall[v] = 0
}
//2.目数组中,存不进去,即重复元素,所有存不进去的集合就是并集
for _, v := range dest {
l := len(mall)
mall[v] = 1
if l != len(mall) { //长度变化,即可以存
l = len(mall)
} else { //存不了,进并集
set = append(set, v)
}
}
//3.遍历交集,在并集中找,找到就从并集中删,删完后就是补集(即并-交=所有变化的元素)
for _, v := range set {
delete(mall, v)
}
//4.此时,mall是补集,所有元素去源中找,找到就是删除的,找不到的必定能在目数组中找到,即新加的
var added, deleted []string
for v, _ := range mall {
_, exist := msrc[v]
if exist {
deleted = append(deleted, v)
} else {
added = append(added, v)
}
}
return added, deleted
}
运行结果:
add: [6 3]
rem: [1 5 9]