Go实现 Bit 数组(集合)
Bit 数组(集合)
本例子来源于《Go语言圣经》。
提示:bitmap(位图)也是这样实现的。
FeelingLife的 inset(集合)
Go 语言里的集合一般会用map[T]bool
这种形式来表示,T 代表元素类型。集合用 map 类型来表示虽然非常灵活,但我们可以以一种更好的形式来表示它。例如在数据流分析领域,集合元素通常是一个非负整数,集合会包含很多元素,并且集合会经常进行并集、交集操作,这种情况下,bit 数组会比 map 表现更加理想。
实现intset
一个 bit 数组通常会用一个无符号数或者称之为“字”的 slice 来表示,每一个元素的每一位都表示集合里的一个值。当集合的第i位被设置时,我们才说这个集合包含元素i。下面的这个程序展示了一个简单的bit数组类型,并且实现了三个函数来对这个 bit 数组来进行操作:
gopl.io/ch6/intset
// unitSize is unit size
const unitSize = 32 << (^uint(0) >> 63)// An IntSet is a set of small non-negative integers.
// Its zero value represents the empty set.
type IntSet struct {words []uint
}// Has reports whether the set contains the non-negative value x.
func (s *IntSet) Has(x int) bool {word, bit := x/unitSize, uint(x%unitSize)return word < len(s.words) && s.words[word]&(1<<bit) != 0
}// Add adds the non-negative value x to the set.
func (s *IntSet) Add(x int) {word, bit := x/unitSize, uint(x%unitSize)for word >= len(s.words) {s.words = append(s.words, 0)}s.words[word] |= 1 << bit
}
因为每一个字都有 64 个二进制位,所以为了定位 x 的 bit 位,我们用了x/64
的商作为字的下标,并且用x%64
得到的值作为这个字内的bit的所在位置。
32 << (^uint(0) >> 63)
表达式可以智能判断平台是 32 位还是 64 位。
var x, y insert.IntSet
x.Add(1)
x.Add(144)
x.Add(9)
fmt.Println(x.String()) // "{1 9 144}"y.Add(9)
y.Add(42)
fmt.Println(y.String()) // "{9 42}"fmt.Println(x.Has(9), x.Has(123)) // "true false"
String
方法
为 Bit 数组实现String
方法:
// String returns the set as a string of the form "{1 2 3}".
func (s *IntSet) String() string {var buf bytes.Bufferbuf.WriteByte('{')for i, word := range s.words {if word == 0 {continue}for j := 0; j < unitSize; j++ {if word&(1<<uint(j)) != 0 {if buf.Len() > len("{") {buf.WriteByte(' ')}fmt.Fprintf(&buf, "%d", unitSize*i+j)}}}buf.WriteByte('}')return buf.String()
}
这里忽略掉fmt.Fprintf
的报错。
Len
、Remove
、Clear
和Copy
方法
// Len returns the numbers of elements.
func (s *IntSet) Len() int {result := 0for _, word := range s.words {if word == 0 {continue}for j := 0; j < unitSize; j++ {if word&(1<<uint(j)) != 0 {result++}}}return result
}// Elems returns all element of the set.
func (s *IntSet) Elems() []int {result := make([]int, 0)for i, word := range s.words {if word == 0 {continue}for j := 0; j < unitSize; j++ {if word&(1<<uint(j)) != 0 {result = append(result, i*unitSize+j)}}}return result
}// Remove removes x from the set.
func (s *IntSet) Remove(x int) {word, bit := x/unitSize, uint(x%unitSize)if word > len(s.words) {return}s.words[word] &^= 1 << bit
}// Clear removes all elements from the set.
func (s *IntSet) Clear() {s.words = nil
}// Copy copies the set and returns the replicated set.
func (s *IntSet) Copy() *IntSet {newWords := make([]uint, len(s.words))copy(newWords, s.words)return &IntSet{words: newWords}
}
这几个方法都是通过 bit 操作实现的。
fmt.Println(x.Len(), y.Len()) // 3 2
x.Remove(1)
y.Remove(42)
fmt.Println(x.String()) // "{9 144}"
fmt.Println(y.String()) // "{9}"
xx := x.Copy()
x.Clear()
fmt.Println(x.String()) // "{}"
fmt.Println(xx.String()) // "{9 42}"
变参方法
// AddAll adds the no-negative values to the set.
func (s *IntSet) AddAll(values ...int) {for _, value := range values {s.Add(value)}
}
x.AddAll(1, 2, 3, 4)
fmt.Println(x.String())
并集
并集:将 A 集合和 B 集合的元素合并在一起。
// UnionWith sets s to the union of s and t.
func (s *IntSet) UnionWith(t *IntSet) {/*s: 1 0 1 1 0t: 1 1 0 1 0s: 1 1 1 1 0*/for i, tword := range t.words {if i < len(s.words) {s.words[i] |= tword} else {s.words = append(s.words, tword)}}
}
UnionWith
这个方法里用到了 bit 位的“或”逻辑操作符号|来一次完成 64 个元素的或计算。
x.UnionWith(&y)
fmt.Println(x.String()) // "{1 9 42 144}"
交集
交集:元素在 A 集合 B 集合均出现。
// IntersectWith sets s to the intersection of s and t.
func (s *IntSet) IntersectWith(t *IntSet) {/*s: 1 0 1 1 0t: 1 1 0 1 0s: 1 0 0 1 0*/minLen := len(s.words)if len(t.words) < minLen {minLen = len(t.words)}for i, tword := range t.words {if i == minLen {break}s.words[i] &= s.words[i] & tword}for i := minLen; i < len(s.words); i++ {s.words[i] = 0}
}
x.IntersectWith(&y)
fmt.Printf(x.String()) // "{9}"
差集
差集:元素出现在 A 集合,未出现在 B 集合。
// DifferenceWith sets s to the difference of s and t.
func (s *IntSet) DifferenceWith(t *IntSet) {/*s: 1 0 1 1 0t: 1 1 0 1 0s: 0 0 1 0 0*/for i, tword := range t.words {if i < len(s.words) {s.words[i] &= s.words[i] ^ tword}}
}
x.DifferenceWith(&y)
fmt.Printf(x.String()) // "{1 144}"
并差集
并差集:元素出现在 A 但没有出现在 B,或者出现在 B 没有出现在 A。
// SymmetricDifference sets s to the union difference of s and t.
func (s *IntSet) SymmetricDifference(t *IntSet) {/*s: 1 0 1 1 0t: 1 1 0 1 0s: 0 1 1 0 0*/for i, tword := range t.words {if i < len(s.words) {s.words[i] = s.words[i]&(s.words[i]^tword) | (tword & (s.words[i] ^ tword))} else {s.words = append(s.words, tword)}}
}
x.SymmetricDifference(&y)
fmt.Printf(x.String()) // "{1 42 144}"
Go实现 Bit 数组(集合)相关推荐
- (1)学习数组,集合,IEnumerable接口,引申学习迭代器
发展:数组-->集合-->泛型 (1)数组 1. 数组数据结构是System.Array类的一个实例. 2. System.Array类的语法为 [SerializableAttribut ...
- Java的数组集合概括
Java的数组集合概括 Collection 1.List(存储有序,有索引,可以重复) 1.1.ArrayList 底层是数组实现的,线程不安全,查找和修改快,增删比较慢 1.2.LinkedLis ...
- SpringMVC请求中的普通、POJO、数组集合类型传参与类转换器
SpringMVC将传递的参数封装到处理器方法的形参中,达到快速访问参数的目的. 普通类型参数传参 参数名与处理器方法形参名保持一致 访问URL: http://localhost/requestPa ...
- java 数组覆盖_JavaSE——数组集合
原标题:JavaSE--数组集合 声明:本栏目所使用的素材都是凯哥学堂VIP学员所写,学员有权匿名,对文章有最终解释权:凯哥学堂旨在促进VIP学员互相学习的基础上公开笔记. Arrays:介绍:数组的 ...
- java二分查找算法字符串数组_Java 算法——二分查找数组集合关键元素
packagecom.sinosoft;import java.util.*;importjava.util.stream.Stream;/***@authorCreated by xushuyi * ...
- jQuery遍历对象/数组/集合
jQuery遍历对象/数组/集合 转载自: 网络1.jquery 遍历对象<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitiona ...
- Gson系列1 --- Gson 序列化与反序列化 -- 数组 / 集合序列化
1.简述 > 数组 / 集合序列化 采用数组的形式 gson.fromJson(json, XXX[].class);采用集合List的形式 gson.fromJson(json, new Ty ...
- C#方法,可空类型,数组,集合,ArrayList排序,List,Hashtable和Dictionary
C#方法 方法的定义: public void/int Compare(int a,int b){ } Program program = new Program(); Console.WriteLi ...
- Dart list数组集合类型
Dart数据类型: List(数组/集合) List里面常用的属性和方法: 常用属性:length 长度reversed 翻转isEmpty 是否为空isNotEmpty 是否不为空 常用方法: ad ...
- arcgis中判断某一字段值是否在list数组集合里(地理国情监测):
arcgis中判断某一字段值是否在list数组集合里(地理国情监测): 在select by attribute工具: "Hebing" NOT IN ('4201G国道','42 ...
最新文章
- web.config文件
- 蓝桥杯 基础练习 十进制转十六进制(水题,进制转换)
- 【BZOJ2117】 [2010国家集训队]Crash的旅游计划
- 将EditText的光标定位到字符的最后面
- CIFAR-10数据集可视化二进制版本
- linux ftp 150 无响应,FTP遇到150无响应
- [转]Linux下用gcc/g++生成静态库和动态库(Z)
- android动画之从源码角度分析动画原理
- Adams安装出错的一种情况
- Mac版Lync无法登陆问题(登录设置)
- 告诉你一个真实的Google
- Go语言之工具Go Playground
- 【一坨理论AC的题】Orz sxy大佬
- HSI、HSV、RGB、CMY、CMYK、HSL、HSB、Ycc、XYZ、Lab、YUV颜色模型
- DICOM的理解与学习2
- 手机怎么申请邮箱?手机邮箱下载
- 频谱、频谱密度、能量谱密度、功率谱密度
- Spring获取应用上下文通用类SpringContextHolder
- 详解Cookie、Session和缓存的关系(转)
- 看完这篇你一定能掌握Linux(非广告)