集合
Go語言里的集合一般會用map[T]bool這種形式來表示,T代表元素類型。
集合用map類型來表示雖然非常靈活,但我們可以以一種更好的形式來表示它。例如:在數據流分析領域,集合元素通常是一個非負整數,集合會包含很多元素,並且集合會經常進行並集、交集操作,這種情況下,bit數組會比map表現更加理想
我們知道在go語言中,出了幾本數據類型外; 還有slice,map,struct,interface這些復雜數據類型;
但是我們發現在go中好像沒有集合(set)這種數據類型,那么我們該如何通過go的基礎類型和復雜數據類型來實現集合呢?
下面看一個int類型集合的實現
type IntSet struct {
words []uint64
}
func (s *IntSet) Has(x int) bool {
word, bit := x/64, uint(x%64)
return word < len(s.words) && s.words[word]&(1<<bit) != 0
}
func (s *IntSet) Add(x int) {
word, bit := x/64 ,uint(x%64)
for word >= len(s.words) {
s.words = append(s.words, 0)
}
s.words[word] |= 1 << bit
}
func (s *IntSet) UnionWith(t *IntSet) {
for i, tword := range t.words {
if i < len(s.words) {
s.words[i] |= tword
} else {
s.words = append(s.words, tword)
}
}
}
func (s *IntSet) String() string {
var buf bytes.Buffer
buf.WriteByte('{')
for i, word := range s.words {
if word == 0 {
continue
}
for j := 0; j < 64; j++ {
if word&(1 << uint(j)) != 0 {
if buf.Len() > len("{") {
buf.WriteByte(' ')
}
fmt.Fprintf(&buf, "%d", 64 * i + j)
}
}
}
buf.WriteByte('}')
return buf.String()
}
方法
上面的代碼定義了一個IntSet的結構體,結構體中的字段是一個[]int64的切片,用於存儲int64類型的整型數據;
func (s *IntSet) Has(x int) bool
為IntSet結構體提供了一個方法,用於判斷集合內是否有某個元素了;
func (s *IntSet) Add(x int)
為IntSet結構體提供了添加元素的方法;
func (s *IntSet) UnionWith(t *IntSet)
為IntSet結構體提供了一個方法,用於將2個IntSet類型的集合做並集;
設計思路
一個bit數組通常會用一個無符號數或者稱之為“字”的slice來表示,每一個元素的每一位都表示集合里的一個值。當集合的第i位被設置時,我們才說這個集合包含元素i
因為每一個字都有64個二進制位,所以為了定位x的bit位,我們用了x/64的商作為字的下標,並且用x%64得到的值作為這個字內的bit的所在位置。
UnionWith這個方法里用到了bit位的“或”邏輯操作符號|來一次完成64個元素的或計算