字典

我们翻阅书籍时,很多时候都要查找目录,然后定位到我们要的页数,比如我们查找某个英文单词时,会从英语字典里查看单词表目录,然后定位到词的那一页。

计算机中,也有这种需求。

一、字典

字典是存储键值对的数据结构,把一个键和一个值映射起来,一一映射,键不能重复。在某些教程中,这种结构可能称为符号表,关联数组或映射。我们暂且称它为字典,较好理解。

如:

键=>值

"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文件。

我们会在《查找算法》章节:散列查找和红黑树中,具体分析字典的两种实现方式。

二、实现不可重复集合 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

数据结构和算法(Golang实现)(16)常见数据结构-字典的更多相关文章

  1. 数据结构和算法(Golang实现)(11)常见数据结构-前言

    常见数据结构及算法 数据结构主要用来组织数据,也作为数据的容器,载体. 各种各样的算法,都需要使用一定的数据结构来组织数据. 常见的典型数据结构有: 链表 栈和队列 树 图 上述可以延伸出各种各样的术 ...

  2. 数据结构和算法(Golang实现)(12)常见数据结构-链表

    链表 讲数据结构就离不开讲链表.因为数据结构是用来组织数据的,如何将一个数据关联到另外一个数据呢?链表可以将数据和数据之间关联起来,从一个数据指向另外一个数据. 一.链表 定义: 链表由一个个数据节点 ...

  3. 数据结构和算法(Golang实现)(13)常见数据结构-可变长数组

    可变长数组 因为数组大小是固定的,当数据元素特别多时,固定的数组无法储存这么多的值,所以可变长数组出现了,这也是一种数据结构.在Golang语言中,可变长数组被内置在语言里面:切片slice. sli ...

  4. 数据结构和算法(Golang实现)(14)常见数据结构-栈和队列

    栈和队列 一.栈 Stack 和队列 Queue 我们日常生活中,都需要将物品排列,或者安排事情的先后顺序.更通俗地讲,我们买东西时,人太多的情况下,我们要排队,排队也有先后顺序,有些人早了点来,排完 ...

  5. 数据结构和算法(Golang实现)(15)常见数据结构-列表

    列表 一.列表 List 我们又经常听到列表 List数据结构,其实这只是更宏观的统称,表示存放数据的队列. 列表List:存放数据,数据按顺序排列,可以依次入队和出队,有序号关系,可以取出某序号的数 ...

  6. 数据结构和算法(Golang实现)(17)常见数据结构-树

    树 树是一种比较高级的基础数据结构,由n个有限节点组成的具有层次关系的集合. 树的定义: 有节点间的层次关系,分为父节点和子节点. 有唯一一个根节点,该根节点没有父节点. 除了根节点,每个节点有且只有 ...

  7. 数据结构和算法(Golang实现)(25)排序算法-快速排序

    快速排序 快速排序是一种分治策略的排序算法,是由英国计算机科学家Tony Hoare发明的, 该算法被发布在1961年的Communications of the ACM 国际计算机学会月刊. 注:A ...

  8. 数据结构和算法(Golang实现)(1)简单入门Golang-前言

    数据结构和算法在计算机科学里,有非常重要的地位.此系列文章尝试使用 Golang 编程语言来实现各种数据结构和算法,并且适当进行算法分析. 我们会先简单学习一下Golang,然后进入计算机程序世界的第 ...

  9. 数据结构和算法(Golang实现)(2)简单入门Golang-包、变量和函数

    包.变量和函数 一.举个例子 现在我们来建立一个完整的程序main.go: // Golang程序入口的包名必须为 main package main // import "golang&q ...

随机推荐

  1. Peek和Pop功能开发(3D Touch开发之一)

    1.哪些设备支持3D Touch iPhone 6s/Plus.iPhone 7s/Plus.iPhone 8s/Plus.iPhone X (系统必须是iOS9或者更新的版本) 2.怎么实现Peek ...

  2. PyCharm导入selenium的webdirver模块出错

    selenium安装好后准备验证一下,导入的webdriver有红色标识,检查了浏览器驱动的.环境变量仍然没有解决问题. 后来在百度找到了原因:项目没安装selenium Interpreter 打开 ...

  3. MySQL----DML(增删改表中数据)

    ##DML:增删改表中的数据 1.添加数据 *语法: *  insert into 表名(列名1,列名2,...列名n) values (值1,值2,...值n); *注意: 1.列名和值要一一对应. ...

  4. Python第五章-内置数据结构03-元组

    Python 内置的数据结构 三.元组(tuple) python 作为一个发展中的语言,也提供了其他的一些数据类型. tuple也是 python 中一个标准的序列类型. 他的一些操作和str和li ...

  5. UNIX环境高级编程——TCP/IP网络编程 常用网络信息检索函数

    UNIX环境高级编程——TCP/IP网络编程   常用网络信息检索函数 gethostname()   getppername()   getsockname()   gethostbyname() ...

  6. 用Arcgis缓存文件发布服务

    切片文件类型为: 其中_alllayers中为各等级的切片文件,xml文件为切片信息(级别).发布服务的步骤如下: 1.将文件拷贝到服务器上: 2.将gdb和xml文件拷贝出来,放到桌面上: 3.打开 ...

  7. python中的函数及作用域的理解

    内置函数 常用的几个内置函数 function des len 求长度 min 求最小值 max 求最大值 sorted 排序 reversed 反向 sum 求和 进制转换函数 function d ...

  8. Redis 主从复制机制(集群)与 哨兵机制

    1,什么是redis 主从复制 支持多个数据库之间的数据同步.只能一个主数据库(master),可以一个或者多个从数据库(slave) 主数据库,可以读写 从数据库,只可以读 当主数据库,做写的操作的 ...

  9. POJ旅行商问题——解题报告

    旅行商问题 总时间限制: 1000ms 内存限制: 65536kB 描述 某国家有n(1<=n<=10)座城市,给定任意两座城市间距离(不超过1000的非负整数).一个旅行商人希望访问每座 ...

  10. EF Core 数据变更自动审计设计

    EF Core 数据变更自动审计设计 Intro 有的时候我们需要知道每个数据表的变更记录以便做一些数据审计,数据恢复以及数据同步等之类的事情, EF 自带了对象追踪,使得我们可以很方便的做一些审计工 ...