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的报错。

LenRemoveClearCopy方法

// 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. (1)学习数组,集合,IEnumerable接口,引申学习迭代器

    发展:数组-->集合-->泛型 (1)数组 1. 数组数据结构是System.Array类的一个实例. 2. System.Array类的语法为 [SerializableAttribut ...

  2. Java的数组集合概括

    Java的数组集合概括 Collection 1.List(存储有序,有索引,可以重复) 1.1.ArrayList 底层是数组实现的,线程不安全,查找和修改快,增删比较慢 1.2.LinkedLis ...

  3. SpringMVC请求中的普通、POJO、数组集合类型传参与类转换器

    SpringMVC将传递的参数封装到处理器方法的形参中,达到快速访问参数的目的. 普通类型参数传参 参数名与处理器方法形参名保持一致 访问URL: http://localhost/requestPa ...

  4. java 数组覆盖_JavaSE——数组集合

    原标题:JavaSE--数组集合 声明:本栏目所使用的素材都是凯哥学堂VIP学员所写,学员有权匿名,对文章有最终解释权:凯哥学堂旨在促进VIP学员互相学习的基础上公开笔记. Arrays:介绍:数组的 ...

  5. java二分查找算法字符串数组_Java 算法——二分查找数组集合关键元素

    packagecom.sinosoft;import java.util.*;importjava.util.stream.Stream;/***@authorCreated by xushuyi * ...

  6. jQuery遍历对象/数组/集合

    jQuery遍历对象/数组/集合 转载自: 网络1.jquery 遍历对象<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitiona ...

  7. Gson系列1 --- Gson 序列化与反序列化 -- 数组 / 集合序列化

    1.简述 > 数组 / 集合序列化 采用数组的形式 gson.fromJson(json, XXX[].class);采用集合List的形式 gson.fromJson(json, new Ty ...

  8. C#方法,可空类型,数组,集合,ArrayList排序,List,Hashtable和Dictionary

    C#方法 方法的定义: public void/int Compare(int a,int b){ } Program program = new Program(); Console.WriteLi ...

  9. Dart list数组集合类型

    Dart数据类型: List(数组/集合) List里面常用的属性和方法: 常用属性:length 长度reversed 翻转isEmpty 是否为空isNotEmpty 是否不为空 常用方法: ad ...

  10. arcgis中判断某一字段值是否在list数组集合里(地理国情监测):

    arcgis中判断某一字段值是否在list数组集合里(地理国情监测): 在select by attribute工具: "Hebing" NOT IN ('4201G国道','42 ...

最新文章

  1. web.config文件
  2. 蓝桥杯 基础练习 十进制转十六进制(水题,进制转换)
  3. 【BZOJ2117】 [2010国家集训队]Crash的旅游计划
  4. 将EditText的光标定位到字符的最后面
  5. CIFAR-10数据集可视化二进制版本
  6. linux ftp 150 无响应,FTP遇到150无响应
  7. [转]Linux下用gcc/g++生成静态库和动态库(Z)
  8. android动画之从源码角度分析动画原理
  9. Adams安装出错的一种情况
  10. Mac版Lync无法登陆问题(登录设置)
  11. 告诉你一个真实的Google
  12. Go语言之工具Go Playground
  13. 【一坨理论AC的题】Orz sxy大佬
  14. HSI、HSV、RGB、CMY、CMYK、HSL、HSB、Ycc、XYZ、Lab、YUV颜色模型
  15. DICOM的理解与学习2
  16. 手机怎么申请邮箱?手机邮箱下载
  17. 频谱、频谱密度、能量谱密度、功率谱密度
  18. Spring获取应用上下文通用类SpringContextHolder
  19. 详解Cookie、Session和缓存的关系(转)
  20. 看完这篇你一定能掌握Linux(非广告)

热门文章

  1. Opengl投影变换理解
  2. Python将列表元素按指定个数分组小技巧
  3. 华亭一中2021年高考成绩查询,华亭一中2019高考又传佳讯!
  4. 1095 解码PAT准考证
  5. IDEA上maven窗口上的图标都是什么意思
  6. CSS :浮动与清除浮动
  7. 大文件上传时如何做到秒传?
  8. 马肯9450命令回传
  9. 存档属性是做什么用的?
  10. FPGA的设计艺术(7)STA实战之SmartTime时序约束及分析示例(II)