數據結構和算法(Golang實現)(16)常見數據結構-字典

字典

咱們翻閱書籍時,不少時候都要查找目錄,而後定位到咱們要的頁數,好比咱們查找某個英文單詞時,會從英語字典裏查看單詞表目錄,而後定位到詞的那一頁。算法

計算機中,也有這種需求。編程

1、字典

字典是存儲鍵值對的數據結構,把一個鍵和一個值映射起來,一一映射,鍵不能重複。在某些教程中,這種結構可能稱爲符號表,關聯數組或映射。咱們暫且稱它爲字典,較好理解。segmentfault

如:數組

鍵=>值

"cat"=>2
"dog"=>1
"hen"=>3

咱們拿出鍵cat的值,就是2了。安全

Golang提供了這一數據結構:map,而且要求鍵的數據類型必須是可比較的,由於若是不可比較,就沒法知道鍵是存在仍是不存在。數據結構

Golang字典的通常的操做以下:併發

package main

import "fmt"

func main() {
    // 新建一個容量爲4的字典 map
    m := make(map[string]int64, 4)

    // 放三個鍵值對
    m["dog"] = 1
    m["cat"] = 2
    m["hen"] = 3

    fmt.Println(m)

    // 查找 hen
    which := "hen"
    v, ok := m[which]
    if ok {
        // 找到了
        fmt.Println("find:", which, "value:", v)
    } else {
        // 找不到
        fmt.Println("not find:", which)
    }

    // 查找 ccc
    which = "ccc"
    v, ok = m[which]
    if ok {
        // 找到了
        fmt.Println("find:", which, "value:", v)
    } else {
        // 找不到
        fmt.Println("not find:", which)
    }
}

字典的實現有兩種方式:哈希表HashTable和紅黑樹RBTreeGolang語言中字典map的實現由哈希表實現,具體可參考標準庫runtime下的map.go文件。app

咱們會在《查找算法》章節:散列查找和紅黑樹中,具體分析字典的兩種實現方式。數據結構和算法

2、實現不可重複集合 Set

通常不少編程語言庫,會把不可重複集合(Collection)命名爲Set,這個Set中文直譯爲集合,在某些上下文條件下,咱們大腦要自動過濾,集合這詞指的是不可重複集合仍是指統稱的集合,在這裏均可以看到中文博大精深。編程語言

不可重複集合Set存放數據,特色就是沒有數據會重複,會去重。你放一個數據進去,再放一個數據進去,若是兩個數據同樣,那麼只會保存一份數據。

集合Set能夠沒有順序關係,也能夠按值排序,算一種特殊的列表。

由於咱們知道字典的鍵是不重複的,因此只要咱們不考慮字典的值,就能夠實現集合,咱們來實現存整數的集合Set

// 集合結構體
type Set struct {
    m            map[int]struct{} // 用字典來實現,由於字段鍵不能重複
    len          int          // 集合的大小
    sync.RWMutex              // 鎖,實現併發安全
}

2.1.初始化一個集合

// 新建一個空集合
func NewSet(cap int64) *Set {
    temp := make(map[int]struct{}, cap)
    return &Set{
        m: temp,
    }
}

使用一個容量爲capmap來實現不可重複集合。map的值咱們不使用,因此值定義爲空結構體struct{},由於空結構體不佔用內存空間。如:

package main

import (
    "fmt"
    "sync"
)

func main()
    // 爲何使用空結構體
    a := struct{}{}
    b := struct{}{}
    if a == b {
        fmt.Printf("right:%p\n", &a)
    }

    fmt.Println(unsafe.Sizeof(a))
}

會打印出:

right:0x1198a98
0

空結構體的內存地址都同樣,而且不佔用內存空間。

2.2.添加一個元素

// 增長一個元素
func (s *Set) Add(item int) {
    s.Lock()
    defer s.Unlock()
    s.m[item] = struct{}{} // 實際往字典添加這個鍵
    s.len = len(s.m)       // 從新計算元素數量
}

首先,加併發鎖,實現線程安全,而後往結構體s *Set裏面的內置map添加該元素:item,元素做爲字典的鍵,會自動去重。同時,集合大小從新生成。

時間複雜度等於字典設置鍵值對的複雜度,哈希不衝突的時間複雜度爲:O(1),不然爲O(n),可看哈希表實現一章。

2.3.刪除一個元素

// 移除一個元素
func (s *Set) Remove(item int) {
    s.Lock()
    s.Unlock()

    // 集合沒元素直接返回
    if s.len == 0 {
        return
    }

    delete(s.m, item) // 實際從字典刪除這個鍵
    s.len = len(s.m)  // 從新計算元素數量
}

同理,先加併發鎖,而後刪除map裏面的鍵:item。時間複雜度等於字典刪除鍵值對的複雜度,哈希不衝突的時間複雜度爲:O(1),不然爲O(n),可看哈希表實現一章。

2.3.查看元素是否在集合中

// 查看是否存在元素
func (s *Set) Has(item int) bool {
    s.RLock()
    defer s.RUnlock()
    _, ok := s.m[item]
    return ok
}

時間複雜度等於字典獲取鍵值對的複雜度,哈希不衝突的時間複雜度爲:O(1),不然爲O(n),可看哈希表實現一章。

2.4.查看集合大小

// 查看集合大小
func (s *Set) Len() int {
    return s.len
}

時間複雜度:O(1)

2.5.查看集合是否爲空

// 集合是夠爲空
func (s *Set) IsEmpty() bool {
    if s.Len() == 0 {
        return true
    }
    return false
}

時間複雜度:O(1)

2.6.清除集合全部元素

// 清除集合全部元素
func (s *Set) Clear() {
    s.Lock()
    defer s.Unlock()
    s.m = map[int]struct{}{} // 字典從新賦值
    s.len = 0                // 大小歸零
}

將原先的map釋放掉,而且從新賦一個空的map

時間複雜度:O(1)

2.7.將集合轉化爲列表

func (s *Set) List() []int {
    s.RLock()
    defer s.RUnlock()
    list := make([]int, 0, s.len)
    for item := range s.m {
        list = append(list, item)
    }
    return list
}

時間複雜度:O(n)

2.8.完整例子

package main

import (
    "fmt"
    "sync"
    "unsafe"
)

// 集合結構體
type Set struct {
    m            map[int]struct{} // 用字典來實現,由於字段鍵不能重複
    len          int              // 集合的大小
    sync.RWMutex                  // 鎖,實現併發安全
}

// 新建一個空集合
func NewSet(cap int64) *Set {
    temp := make(map[int]struct{}, cap)
    return &Set{
        m: temp,
    }
}

// 增長一個元素
func (s *Set) Add(item int) {
    s.Lock()
    defer s.Unlock()
    s.m[item] = struct{}{} // 實際往字典添加這個鍵
    s.len = len(s.m)       // 從新計算元素數量
}

// 移除一個元素
func (s *Set) Remove(item int) {
    s.Lock()
    s.Unlock()

    // 集合沒元素直接返回
    if s.len == 0 {
        return
    }

    delete(s.m, item) // 實際從字典刪除這個鍵
    s.len = len(s.m)  // 從新計算元素數量
}

// 查看是否存在元素
func (s *Set) Has(item int) bool {
    s.RLock()
    defer s.RUnlock()
    _, ok := s.m[item]
    return ok
}

// 查看集合大小
func (s *Set) Len() int {
    return s.len
}

// 清除集合全部元素
func (s *Set) Clear() {
    s.Lock()
    defer s.Unlock()
    s.m = map[int]struct{}{} // 字典從新賦值
    s.len = 0                // 大小歸零
}

// 集合是夠爲空
func (s *Set) IsEmpty() bool {
    if s.Len() == 0 {
        return true
    }
    return false
}

// 將集合轉化爲列表
func (s *Set) List() []int {
    s.RLock()
    defer s.RUnlock()
    list := make([]int, 0, s.len)
    for item := range s.m {
        list = append(list, item)
    }
    return list
}

// 爲何使用空結構體
func other() {
    a := struct{}{}
    b := struct{}{}
    if a == b {
        fmt.Printf("right:%p\n", &a)
    }

    fmt.Println(unsafe.Sizeof(a))
}

func main() {
    //other()

    // 初始化一個容量爲5的不可重複集合
    s := NewSet(5)

    s.Add(1)
    s.Add(1)
    s.Add(2)
    fmt.Println("list of all items", s.List())

    s.Clear()
    if s.IsEmpty() {
        fmt.Println("empty")
    }

    s.Add(1)
    s.Add(2)
    s.Add(3)

    if s.Has(2) {
        fmt.Println("2 does exist")
    }

    s.Remove(2)
    s.Remove(3)
    fmt.Println("list of all items", s.List())
}

打印出:

list of all items [1 2]
empty
2 does exist
list of all items [1]

系列文章入口

我是陳星星,歡迎閱讀我親自寫的 數據結構和算法(Golang實現),文章首發於 閱讀更友好的GitBook

相關文章
相關標籤/搜索