├── .appveyor.yml ├── .gitignore ├── .revive.toml ├── .travis.yml ├── LICENSE ├── README.md ├── avl_tree.go ├── avl_tree_test.go ├── counter.go ├── counter_test.go ├── deque.go ├── deque_test.go ├── lifo_queue.go ├── lifo_queue_test.go ├── ordered_map.go ├── ordered_map_O_test.go ├── ordered_map_test.go ├── priority_queue.go ├── priority_queue_test.go ├── queue.go ├── queue_test.go ├── sort.go ├── sort_test.go └── std_sort.go /.appveyor.yml: -------------------------------------------------------------------------------- 1 | build: off 2 | 3 | clone_folder: c:\gopath\src\github.com\chenjiandongx\collections 4 | 5 | environment: 6 | GOPATH: c:\gopath 7 | 8 | stack: go 1.10 9 | 10 | before_test: 11 | - go get -t -v ./... 12 | - go vet ./... 13 | 14 | test_script: 15 | - go test -v -race ./... 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # IDE 15 | .idea/ 16 | .vscode/ 17 | -------------------------------------------------------------------------------- /.revive.toml: -------------------------------------------------------------------------------- 1 | ignoreGeneratedHeader = false 2 | severity = "warning" 3 | confidence = 0.8 4 | errorCode = 0 5 | warningCode = 0 6 | 7 | [rule.blank-imports] 8 | [rule.context-as-argument] 9 | [rule.context-keys-type] 10 | [rule.dot-imports] 11 | [rule.error-return] 12 | [rule.error-strings] 13 | [rule.error-naming] 14 | [rule.if-return] 15 | [rule.increment-decrement] 16 | [rule.var-naming] 17 | [rule.var-declaration] 18 | [rule.package-comments] 19 | [rule.range] 20 | [rule.receiver-naming] 21 | [rule.time-naming] 22 | [rule.unexported-return] 23 | [rule.indent-error-flow] 24 | [rule.errorf] -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: go 3 | go: 4 | - 1.9.x 5 | - 1.10.x 6 | - 1.11.x 7 | install: 8 | - # Do nothing. This is needed to prevent default install action 9 | - # "go get -t -v ./..." from happening here (we want it to happen inside script step). 10 | script: 11 | - go get -t -v ./... 12 | - diff -u <(echo -n) <(gofmt -d -s .) 13 | - go tool vet . 14 | - go test -v -race ./... 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 chenjiandongx 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 📂 collctions 2 | 3 | > Golang 实现的 collections 模块,灵感来自 [Python queue](https://docs.python.org/3/library/queue.html) 和 [Python collections](https://docs.python.org/3/library/collections.html) 4 | 5 | [![Build Status](https://travis-ci.org/chenjiandongx/collections.svg?branch=master)](https://travis-ci.org/chenjiandongx/collections) [![Build status](https://ci.appveyor.com/api/projects/status/b0qa418u4j502086?svg=true)](https://ci.appveyor.com/project/chenjiandongx/collections) [![Go Report Card](https://goreportcard.com/badge/github.com/chenjiandongx/collections)](https://goreportcard.com/report/github.com/chenjiandongx/collections) [![License: MIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT) [![GoDoc](https://godoc.org/github.com/chenjiandongx/collections?status.svg)](https://godoc.org/github.com/chenjiandongx/collections) 6 | 7 | ## 📚 目录 8 | 9 | * [Queue - 先进先出队列](#Queue) 10 | * [LifoQueue - 后进先出队列](#LifoQueue) 11 | * [PriorityQueue - 优先队列](#PriorityQueue) 12 | * [Deque - 双端队列](#Deque) 13 | * [OrderedMap - 有序 Map](#OrderedMap) 14 | * [Counter - 计数器](#Counter) 15 | * [AVLTree - AVL 树](#AVLTree) 16 | * [Sort - 排序](#Sort) 17 | 18 | ### 🔰 安装&引用 19 | 20 | ```bash 21 | $ go get github.com/chenjiandongx/collections 22 | 23 | import "github.com/chenjiandongx/collections" 24 | ``` 25 | 26 | ### 📦 Collections 27 | 28 | ### Queue 29 | > 先进先出队列(线程安全) 30 | 31 | 📝 方法集 32 | ```shell 33 | Get()(interface{}, bool) // 出队 34 | Put(v interface{}) // 入队 35 | Qsize() int // 返回队列长度 36 | IsEmpty() bool // 判断队列是否为空 37 | ``` 38 | 39 | ✏️ 示例 40 | ```go 41 | var nums = 1000 42 | 43 | q := collections.NewQueue() 44 | var item interface{} 45 | var ok bool 46 | for i := 0; i < nums; i++ { 47 | q.Put(i) 48 | } 49 | for i := 0; i < nums; i++ { 50 | if item, ok = q.Get(); ok { 51 | fmt.Println(item.(int)) 52 | } 53 | } 54 | 55 | fmt.Println(q.IsEmpty()) 56 | fmt.Println(q.Qsize()) 57 | ``` 58 | 59 | ### LifoQueue 60 | > 后进先出队列(线程安全) 61 | 62 | 📝 方法集 63 | ```shell 64 | Get()(interface{}, bool) // 出队 65 | Put(v interface{}) // 入队 66 | Qsize() int // 返回队列长度 67 | IsEmpty() bool // 判断队列是否为空 68 | ``` 69 | 70 | ✏️ 示例 71 | ```go 72 | var nums = 1000 73 | 74 | q := collections.NewLifoQueue() 75 | var item interface{} 76 | var ok bool 77 | for i := 0; i < nums; i++ { 78 | q.Put(i) 79 | } 80 | for i := nums-1; i >=0; i-- { 81 | if item, ok = q.Get(); ok { 82 | fmt.Println(item.(int)) 83 | } 84 | } 85 | 86 | fmt.Println(q.IsEmpty()) 87 | fmt.Println(q.Qsize()) 88 | ``` 89 | 90 | ### PriorityQueue 91 | > 优先队列(线程安全) 92 | 93 | 📝 方法集 94 | ```shell 95 | Get()(interface{}, bool) // 出队 96 | Put(v *PqNode) // 入队 97 | Qsize() int // 返回队列长度 98 | IsEmpty() bool // 判断队列是否为空 99 | 100 | // 优先队列节点 101 | type PqNode struct { 102 | Value string 103 | Priority, index int 104 | } 105 | ``` 106 | 107 | ✏️ 示例 108 | ```go 109 | var nums = 1000 110 | 111 | q := collections.NewPriorityQueue() 112 | 113 | for i := 0; i < nums; i++ { 114 | r := rand.Int() 115 | q.Put(&collections.PqNode{Value: string(r), Priority: rand.Int()}) 116 | } 117 | 118 | for i := 0; i < nums/2; i++ { 119 | item1, _ := q.Get() 120 | item2, _ := q.Get() 121 | fmt.Println(item1.(*collections.PqNode).Priority > item2.(*collections.PqNode).Priority) 122 | } 123 | 124 | fmt.Println(q.IsEmpty()) 125 | fmt.Println(q.Qsize()) 126 | ``` 127 | 128 | ### Deque 129 | > 双端队列(线程安全) 130 | 131 | 📝 方法集 132 | ```shell 133 | GetLeft()(interface{}, bool) // 左边出队 134 | GetRight()(interface{}, bool) // 右边出队 135 | PutLeft(v interface{}) // 左边入队 136 | PutRight(v interface{}) // 右边入队 137 | Qsize() int // 返回队列长度 138 | IsEmpty() bool // 判断队列是否为空 139 | ``` 140 | 141 | ✏️ 示例 142 | ```go 143 | var nums = 1000 144 | q := collections.NewDeque() 145 | 146 | var item interface{} 147 | var ok bool 148 | 149 | for i := 0; i < nums; i++ { 150 | q.PutLeft(i) 151 | } 152 | fmt.Println(q.Qsize()) 153 | 154 | for i := nums - 1; i >= 0; i-- { 155 | q.PutRight(i) 156 | } 157 | fmt.Println(q.Qsize()) 158 | 159 | for i := 0; i < nums; i++ { 160 | item, ok = q.GetRight() 161 | fmt.Println(item, ok) 162 | } 163 | for i := nums - 1; i >= 0; i-- { 164 | item, ok = q.GetLeft() 165 | fmt.Println(item, ok) 166 | } 167 | 168 | item, ok = q.GetLeft() 169 | fmt.Println(item, ok) 170 | 171 | item, ok = q.GetRight() 172 | fmt.Println(item, ok) 173 | ``` 174 | 175 | ### OrderedMap 176 | > 有序 Map,接口设计参考 [cevaris/ordered_map](https://github.com/cevaris/ordered_map) 177 | 178 | 📝 方法集 179 | ```shell 180 | Set(key, value interface{}) // 新增键值对 181 | Get(key interface{}) (interface{}, bool) // 取值 182 | Delete(key interface{}) bool // 删除键 183 | Iter() (interface{}, interface{}, bool) // 遍历 184 | Len() int // 键值对数量 185 | // 指针回退到 Head,遍历时 current 指针会向后移动 BackToHead 使其移动到头指针,以便下一次从头遍历 186 | BackToHead() 187 | ``` 188 | 189 | ✏️ 示例 190 | ```go 191 | maxNum := 100 192 | om := collections.NewOrderedMap() 193 | for i := 0; i < maxNum; i++ { 194 | om.Set(i, i+1) 195 | } 196 | 197 | fmt.Println(om.Len()) 198 | om.Delete(0) 199 | fmt.Println(om.Len()) 200 | 201 | for k, v, ok := om.Iter(); ok; k, v, ok = om.Iter() { 202 | fmt.Println(k, v) 203 | } 204 | 205 | om.BackToHead() 206 | for k, v, ok := om.Iter(); ok; k, v, ok = om.Iter() { 207 | fmt.Println(k, v) 208 | } 209 | ``` 210 | 211 | 📣 讨论 212 | 213 | 有序 Map 在 Golang 中应该是十分常见的需求,Map 最大的优势就是其查找性能,**理论上** Map 查找的时间复杂度为常数级。但实际情况我们可以通过 benchmark 来验证。在 [Go Maps Don’t Appear to be O(1)](https://medium.com/@ConnorPeet/go-maps-are-not-o-1-91c1e61110bf) 这篇文章中,作者测试了 Golang Map 查找的实际性能,不过作者是基于 Go1.4 的,版本有点旧了。下面是我修改了作者的测试案例后在 Go1.10 下跑出来的结果。 214 | 215 | ![](https://user-images.githubusercontent.com/19553554/51075377-83f8cd80-16c5-11e9-9973-4904a4661aeb.png) 216 | 217 | 上图是使用 [go-echarts](https://github.com/go-echarts/go-echarts) 绘制的。测试通过与二分查找对比,二分查找的时间复杂度为 **O(log2n)**。很明显,在 10e5 数量级下两者的性能差别还不是特别大,主要差距是在 10e6 后体现的。结论:Map 的性能优于 **O(log2n)**,但不是常数级。 218 | 219 | **collections.OrderdMap 🆚 cevaris/ordered_map** 220 | 221 | 本来我一直使用的是 [cevaris/ordered_map](https://github.com/cevaris/ordered_map),后来自己重新实现了一个。实现完就与其进行了性能测试对比,它是基于两个 Map 实现的,而我是使用的 Map+LinkedList,LinkedList 在删除和插入操作上的时间复杂度都是 **O(1)**,用其来存储 Map key 的顺序是一个很好的选择。 222 | 223 | 同样的测试代码,BenchMark 结果如下 224 | ```shell 225 | goos: windows 226 | goarch: amd64 227 | pkg: github.com/chenjiandongx/collections 228 | BenchmarkCollectionsSet-8 2000000 689 ns/op 187 B/op 3 allocs/op 229 | BenchmarkCevarisSet-8 1000000 1212 ns/op 334 B/op 3 allocs/op 230 | BenchmarkCollectionsGet-8 2000000 823 ns/op 187 B/op 3 allocs/op 231 | BenchmarkCevarisGet-8 1000000 1281 ns/op 334 B/op 3 allocs/op 232 | BenchmarkCollectionsIter-8 2000000 670 ns/op 187 B/op 3 allocs/op 233 | BenchmarkCevarisIter-8 1000000 1341 ns/op 366 B/op 4 allocs/op 234 | ``` 235 | **collections.OrderedMap Win 🖖 性能+内存占用全部占优 🚀** 236 | 237 | ### Counter 238 | > 计数器 239 | 240 | 📝 方法集 241 | ```shell 242 | // key-value item 243 | type Item struct { 244 | k interface{} 245 | v int 246 | } 247 | 248 | Add(keys ...interface{}) // 新增 item 249 | Get(key interface{}) int // 获取 key 计数 250 | GetAll() []Item // 获取全部 key 计数 251 | Top(n int) []Item // 获取前 key 计数 252 | Delete(key interface{}) bool // 删除 key,成功返回 true,key 不存在返回 false 253 | Len() int // key 数量 254 | ``` 255 | 256 | ✏️ 示例 257 | ```go 258 | c := collections.NewCounter() 259 | c.Add("a", "b", "c", "d", "a", "c") 260 | fmt.Println(c.Get("A")) 261 | fmt.Println(c.Get("a")) 262 | fmt.Println(c.Get("b")) 263 | fmt.Println(c.Top(2)) 264 | fmt.Println(c.Len()) 265 | fmt.Println(c.All()) 266 | c.Delete("a") 267 | ``` 268 | 269 | ### AVLTree 270 | > AVL 二叉自平衡查找树 271 | 272 | 📝 方法集 273 | ```shell 274 | NewAVLTree() *AVLTree // 生成 AVL 树 275 | Insert(v int) // 插入节点 276 | Search(v int) bool // 搜索节点 277 | Delete(v int) bool // 删除节点 278 | GetMaxValue() int // 获取所有节点中的最大值 279 | GetMinValue() int // 获取所有节点中的最小值 280 | AllValues() []int // 返回排序后所有值 281 | ``` 282 | 283 | ✏️ 示例 284 | ```go 285 | var maxNum = 100 286 | 287 | tree := NewAVLTree() 288 | for i := 0; i < maxNum; i++ { 289 | tree.Insert(i) 290 | tree.Insert(maxNum + i) 291 | } 292 | fmt.Println(len(tree.AllValues())) 293 | fmt.Println(tree.GetMaxValue()) 294 | fmt.Println(tree.GetMinValue()) 295 | fmt.Println(tree.Search(50)) 296 | fmt.Println(tree.Search(100)) 297 | fmt.Println(tree.Search(-10)) 298 | fmt.Println(tree.Delete(-10)) 299 | fmt.Println(tree.Delete(10)) 300 | ``` 301 | 302 | 📣 讨论 303 | 304 | AVL 树是自平衡树的一种,其通过左旋和右旋来调整自身的平衡性,使其左右子树的高度差最大不超过 1。AVL 在插入、查找、删除的平时时间复杂度都是 O(logn),在基本的 BST(二叉查找树)中,理想情况的效率也是为 O(logn),但由于操作的性能其实是依赖于树的高度,BST 最坏的情况会导致树退化成链表,此时时间复杂度就变为 O(n),为了解决这个问题,自平衡二叉树应运而生。 305 | 306 | AVL 的主要精髓在于`旋转`,旋转分为 4 种情况,左旋,左旋+右旋,右旋,右旋+左旋。调整树结构后需要重新计算树高。 307 | 308 | **左子树左节点失衡** 309 | > 左左情况 直接右旋 310 | ```shell 311 | x 312 | x => 右旋 x 313 | x x x 314 | ``` 315 | 316 | **左子树右节点失衡** 317 | > 左右情况 先左旋后右旋 318 | ```shell 319 | x x 320 | x => 左旋 x => 右旋 x 321 | x x x x 322 | ``` 323 | 324 | **右子树右节点失衡** 325 | > 右右情况 直接左旋 326 | ```shell 327 | x 328 | x => 左旋 x 329 | x x x 330 | ``` 331 | 332 | **右子树左节点失衡** 333 | > 右左情况 先右旋后左旋 334 | ```shell 335 | x x 336 | x => 右旋 x => 左旋 x 337 | x x x x 338 | ``` 339 | 340 | AVL 主要的性能消耗主要在插入,因为其需要通过旋转来维护树的平衡,但如果使用场景是经常需要排序和查找数据的话,AVL 还是可以展现其良好的性能的。 341 | 342 | **benchmark** 343 | ``` 344 | BenchmarkAVLInsert10e1-6 2000000000 0.00 ns/op 345 | BenchmarkAVLInsert10e2-6 2000000000 0.00 ns/op 346 | BenchmarkAVLInsert10e3-6 2000000000 0.00 ns/op 347 | BenchmarkAVLInsert10e4-6 2000000000 0.02 ns/op 348 | BenchmarkAVLInsert10e5-6 1000000000 0.82 ns/op 349 | BenchmarkAVLSearch-6 2000000000 0.00 ns/op 350 | BenchmarkAVLDelete-6 2000000000 0.00 ns/op 351 | ``` 352 | 353 | ### Sort 354 | 355 | 📝 方法集 356 | ```shell 357 | BubbleSort() // 冒泡排序 358 | InsertionSort() // 插入排序 359 | QuickSort() // 快速排序 360 | ShellSort() // 希尔排序 361 | HeapSort() // 堆排序 362 | MergeSort() // 归并排序 363 | ``` 364 | 365 | ✏️ 示例 366 | ```go 367 | var maxCnt = 10e4 368 | 369 | func yieldRandomArray() []int { 370 | res := make([]int, maxCnt) 371 | for i := 0; i < maxCnt; i++ { 372 | res[i] = rand.Int() 373 | } 374 | return res 375 | } 376 | 377 | BubbleSort(yieldRandomArray()) 378 | InsertionSort(yieldRandomArray()) 379 | QuickSort(yieldRandomArray()) 380 | ShellSort(yieldRandomArray()) 381 | HeapSort(yieldRandomArray()) 382 | MergeSort(yieldRandomArray()) 383 | ``` 384 | 385 | 📣 讨论 386 | 387 | **排序算法时间复杂度比较** 388 | 389 | | 排序算法 | 是否稳定 | 平均 | 最好 | 最差 | 动画演示 | 390 | | -------- | --------- |----------| --------| -------- | ----------- | 391 | | BubbleSort | 是 | O(n^2) | O(n) | O(n^2) | ![](https://upload.wikimedia.org/wikipedia/commons/3/37/Bubble_sort_animation.gif) | 392 | | InsertionSort | 是 | O(n^2) | O(n) | O(n^2) | ![](https://upload.wikimedia.org/wikipedia/commons/2/25/Insertion_sort_animation.gif) | 393 | | QuickSort | 否 | O(nlogn) | O(nlogn) | O(n^2) | ![](https://upload.wikimedia.org/wikipedia/commons/6/6a/Sorting_quicksort_anim.gif) | 394 | | ShellSort | 否 |O(nlogn) | O(n) | O(n^2) | ![](https://upload.wikimedia.org/wikipedia/commons/2/25/Insertion_sort_animation.gif) | 395 | | HeapSort | 否 | O(nlogn) | O(nlogn) | O(nlogn) | ![](https://upload.wikimedia.org/wikipedia/commons/1/1b/Sorting_heapsort_anim.gif) | 396 | | MergeSort | 是 | O(nlogn) | O(nlogn) | O(nlogn) | ![](https://upload.wikimedia.org/wikipedia/commons/c/c5/Merge_sort_animation2.gif) | 397 | 398 | 通过 benchmark 来测试平均排序性能 399 | 400 | **数据随机分布** 401 | ```go 402 | var maxCnt int = 10e4 403 | 404 | func yieldRandomArray(cnt int) []int { 405 | res := make([]int, cnt) 406 | for i := 0; i < cnt; i++ { 407 | res[i] = rand.Int() 408 | } 409 | return res 410 | } 411 | ``` 412 | 413 | 运行结果 414 | ```shell 415 | BenchmarkBubbleSort-8 1 17361549400 ns/op 416 | BenchmarkInsertionSort-8 1 1934826900 ns/op 417 | BenchmarkQuickSort-8 100 10651807 ns/op 418 | BenchmarkShellSort-8 100 16476199 ns/op 419 | BenchmarkHeapSort-8 100 14231607 ns/op 420 | BenchmarkMergeSort-8 100 14840583 ns/op 421 | ``` 422 | 423 | 冒泡和直接插入排序在随机数据集的排序性能最差,为 O(n^2),剩余 4 种排序快排效率最佳,其他 3 者性能很接近。 424 | 425 | **换两种极端的数据分布方式** 426 | 427 | **数据升序分布** 428 | ```go 429 | func yieldArrayAsce(cnt int) []int { 430 | res := make([]int, cnt) 431 | for i := 0; i < cnt; i++ { 432 | res[i] = i 433 | } 434 | return res 435 | } 436 | ``` 437 | 438 | 运行结果 439 | ```shell 440 | BenchmarkBubbleSort-8 5000 266690 ns/op 441 | BenchmarkInsertionSort-8 10000 213429 ns/op 442 | BenchmarkQuickSort-8 1 3291222900 ns/op 443 | BenchmarkShellSort-8 1000 1716406 ns/op 444 | BenchmarkHeapSort-8 200 6806788 ns/op 445 | BenchmarkMergeSort-8 300 4677485 ns/op 446 | ``` 447 | 448 | 在数据基本升序的情况下,冒泡和直接插入排序能够取得良好的性能。而快排就给跪了,就是最差的 O(n^2) 了。 449 | 450 | **数据降序分布** 451 | ```go 452 | func yieldArrayDesc(cnt int) []int { 453 | res := make([]int, cnt) 454 | for i := 0; i < cnt; i++ { 455 | res[i] = cnt-i 456 | } 457 | return res 458 | } 459 | ``` 460 | 461 | 运行结果 462 | ```shell 463 | BenchmarkBubbleSort-8 1 6710048800 ns/op 464 | BenchmarkInsertionSort-8 1 3881599100 ns/op 465 | BenchmarkQuickSort-8 1 3373971200 ns/op 466 | BenchmarkShellSort-8 500 2876371 ns/op 467 | BenchmarkHeapSort-8 200 7081150 ns/op 468 | BenchmarkMergeSort-8 300 4448222 ns/op 469 | ``` 470 | 471 | 在数据基本降序的情况下,冒泡和直接插入排序一如既往的差,快排又给跪了,又是 O(n^2)... 472 | 473 | 那自己实现的排序和 Golang 官方提供的 sort.Sort 排序方法对比,效率如何呢 474 | 475 | 476 | 定义一个 struct,实现 sort.Interface 477 | ```go 478 | import "sort" 479 | 480 | type StdItems struct { 481 | data []int 482 | } 483 | 484 | func (o StdItems) Less(i, j int) bool { 485 | return o.data[i] < o.data[j] 486 | } 487 | 488 | func (o StdItems) Swap(i, j int) { 489 | o.data[i], o.data[j] = o.data[j], o.data[i] 490 | } 491 | 492 | func (o StdItems) Len() int { 493 | return len(o.data) 494 | } 495 | ``` 496 | 497 | 只取 n(logn) 复杂度的排序算法与标准 sort 进行对比 498 | 499 | **数据随机分布** 500 | ```shell 501 | BenchmarkStdSort-8 50 22978524 ns/op 502 | BenchmarkQuickSort-8 100 11648689 ns/op 503 | BenchmarkShellSort-8 100 17353544 ns/op 504 | BenchmarkHeapSort-8 100 14501199 ns/op 505 | BenchmarkMergeSort-8 100 13793086 ns/op 506 | ``` 507 | 508 | 是不是眼前一亮 😂,自己写的快排居然这么厉害,比标准的 sort 快了不止两倍??? 这里出现这种情况的主要原因是 sort 实现了 sort.Interface,该接口需要有三个方法 Less()/Len()/Swap(),而接口的类型转换是有成本的。**通用意味着兼容,兼容意味着妥协,这是专和精权衡后的结果**。当然,标准的 sort 大部分情况的性能都是可接受的。但当你需要追求极致性能的话,自己针对特定需求实现排序算法肯定会是更好的选择。 509 | 510 | **数据升序分布** 511 | ```shell 512 | BenchmarkStdSort-8 200 7285511 ns/op 513 | BenchmarkQuickSort-8 1 3351046900 ns/op 514 | BenchmarkShellSort-8 1000 1679506 ns/op 515 | BenchmarkHeapSort-8 200 6632256 ns/op 516 | BenchmarkMergeSort-8 300 4308582 ns/op 517 | ``` 518 | 519 | 是不是又是眼前一亮 🤣,我去 为什么这次标准的排序比快排快了这么多,官方的排序不也是快排吗?(这个测试结果看起来好像也没人会比快排慢是吧 😅) 520 | 521 | **数据降序分布** 522 | ```shell 523 | BenchmarkStdSort-8 200 7405331 ns/op 524 | BenchmarkQuickSort-8 1 3390954400 ns/op 525 | BenchmarkShellSort-8 500 2900240 ns/op 526 | BenchmarkHeapSort-8 200 7091124 ns/op 527 | BenchmarkMergeSort-8 300 4295169 ns/op 528 | ``` 529 | 530 | emmmmmmm,同上 😓 531 | 532 | 关于官方排序的具体实现,可以参考 [src/sort/sort.go](https://golang.org/src/sort/sort.go),实际上是直接插入排序,快速排序,堆排序和归并排序的组合排序。[这篇文章](https://github.com/polaris1119/The-Golang-Standard-Library-by-Example/blob/master/chapter03/03.1.md) 对这部分有介绍 533 | 534 | 最后,按官方的排序针对自己想要的数据类型排序,但不使用接口那套,对比上面排序中最快的算法以及接口实现的 sort 535 | 536 | **数据随机分布** 537 | ```shell 538 | BenchmarkStdSort-8 100 22649399 ns/op 539 | BenchmarkQuickSort-8 100 10870924 ns/op 540 | BenchmarkStdSortWithoutInterface-8 100 10511605 ns/op 541 | ``` 542 | 543 | **数据升序分布** 544 | ```shell 545 | BenchmarkStdSort-8 200 7006117 ns/op 546 | BenchmarkShellSort-8 1000 1667537 ns/op 547 | BenchmarkStdSortWithoutInterface-8 1000 1619643 ns/op 548 | ``` 549 | 550 | **数据降序分布** 551 | ```shell 552 | BenchmarkStdSort-8 200 7614625 ns/op 553 | BenchmarkShellSort-8 500 3051834 ns/op 554 | BenchmarkStdSortWithoutInterface-8 1000 1689479 ns/op 555 | ``` 556 | 557 | 🖖 [StdSortWithoutInterface](https://github.com/chenjiandongx/collections/blob/master/std_sort.go) 完胜!!! 558 | 559 | 我们还可以进一步思考如何获得更高的排序性能,使用 goroutine 将一个数据切分成两半,分别使用 `StdSortWithoutInterface` 排序,将排序后的结果进行一次归并排序,就可以得到最终的有序数组,这次我们测试的数组长度为 **10e5** 560 | 561 | 为了验证真正的`并行计算` 我们将分别测试 cpu 数量为 1, 2, 8 的情况 562 | ```shell 563 | BenchmarkStdSort 5 260696480 ns/op 564 | BenchmarkStdSort-2 5 246746560 ns/op 565 | BenchmarkStdSort-8 5 248532560 ns/op 566 | BenchmarkStdSortWithoutInterface 10 124666470 ns/op 567 | BenchmarkStdSortWithoutInterface-2 10 120676740 ns/op 568 | BenchmarkStdSortWithoutInterface-8 10 126062650 ns/op 569 | BenchmarkStdSortWithGoroutine 20 125163280 ns/op 570 | BenchmarkStdSortWithGoroutine-2 20 80835825 ns/op 571 | BenchmarkStdSortWithGoroutine-8 20 81232625 ns/op 572 | ``` 573 | 574 | 😎 WOW!!! cpu 数量为 1 时大家相差无几,cpu > 1 以后,goroutine 做到了真正的并行,利用多核进行计算,速度提升了 **1.5** 倍,比默认的 Sort 方法提升了 **4** 倍。喏,这就是算法的魅力。 575 | 576 | ### 📃 License 577 | MIT [©chenjiandongx](http://github.com/chenjiandongx) 578 | -------------------------------------------------------------------------------- /avl_tree.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | type avlNode struct { 4 | h int 5 | value int 6 | left *avlNode 7 | right *avlNode 8 | } 9 | 10 | type AVLTree struct { 11 | tree *avlNode 12 | } 13 | 14 | // 生成 AVL 树 15 | func NewAVLTree() *AVLTree { 16 | return &AVLTree{&avlNode{h: -2}} 17 | } 18 | 19 | // 插入节点 20 | func (a *AVLTree) Insert(v int) { 21 | a.tree = insert(v, a.tree) 22 | } 23 | 24 | // 搜索节点 25 | func (a *AVLTree) Search(v int) bool { 26 | return a.tree.search(v) 27 | } 28 | 29 | // 删除节点 30 | func (a *AVLTree) Delete(v int) bool { 31 | if a.tree.search(v) { 32 | a.tree.delete(v) 33 | return true 34 | } 35 | return false 36 | } 37 | 38 | // 获取所有节点中的最大值 39 | func (a *AVLTree) GetMaxValue() int { 40 | return a.tree.maxNode().value 41 | } 42 | 43 | // 获取所有节点中的最小值 44 | func (a *AVLTree) GetMinValue() int { 45 | return a.tree.minNode().value 46 | } 47 | 48 | // 返回排序后所有值 49 | func (a *AVLTree) AllValues() []int { 50 | return a.tree.values() 51 | } 52 | 53 | func max(a, b int) int { 54 | if a > b { 55 | return a 56 | } 57 | return b 58 | } 59 | 60 | func insert(v int, t *avlNode) *avlNode { 61 | if t == nil { 62 | return &avlNode{value: v} 63 | } 64 | if t.h == -2 { 65 | t.value = v 66 | t.h = 0 67 | return t 68 | } 69 | 70 | cmp := v - t.value 71 | if cmp > 0 { 72 | // 将节点插入到右子树中 73 | t.right = insert(v, t.right) 74 | } else if cmp < 0 { 75 | // 将节点插入到左子树中 76 | t.left = insert(v, t.left) 77 | } 78 | // 维持树平衡 79 | t = t.keepBalance(v) 80 | t.h = max(t.left.height(), t.right.height()) + 1 81 | return t 82 | } 83 | 84 | func (t *avlNode) search(v int) bool { 85 | if t == nil { 86 | return false 87 | } 88 | cmp := v - t.value 89 | if cmp > 0 { 90 | // 如果 v 大于当前节点值,继续从右子树中寻找 91 | return t.right.search(v) 92 | } else if cmp < 0 { 93 | // 如果 v 小于当前节点值,继续从左子树中寻找 94 | return t.left.search(v) 95 | } else { 96 | // 相等则表示找到 97 | return true 98 | } 99 | } 100 | 101 | func (t *avlNode) delete(v int) *avlNode { 102 | if t == nil { 103 | return t 104 | } 105 | cmp := v - t.value 106 | if cmp > 0 { 107 | // 如果 v 大于当前节点值,继续从右子树中删除 108 | t.right = t.right.delete(v) 109 | } else if cmp < 0 { 110 | // 如果 v 小于当前节点值,继续从左子树中删除 111 | t.left = t.left.delete(v) 112 | } else { 113 | // 找到 v 114 | if t.left != nil && t.right != nil { 115 | // 如果该节点既有左子树又有右子树 116 | // 使用右子树中的最小节点取代删除节点,然后删除右子树中的最小节点 117 | t.value = t.right.minNode().value 118 | t.right = t.right.delete(t.value) 119 | } else if t.left != nil { 120 | // 如果只有左子树,则直接删除节点 121 | t = t.left 122 | } else { 123 | // 只有右子树或空树 124 | t = t.right 125 | } 126 | } 127 | 128 | if t != nil { 129 | t.h = max(t.left.height(), t.right.height()) + 1 130 | t = t.keepBalance(v) 131 | } 132 | return t 133 | } 134 | 135 | func (t *avlNode) minNode() *avlNode { 136 | if t == nil { 137 | return nil 138 | } 139 | // 整棵树的最左边节点就是值最小的节点 140 | if t.left == nil { 141 | return t 142 | } else { 143 | return t.left.minNode() 144 | } 145 | } 146 | 147 | func (t *avlNode) maxNode() *avlNode { 148 | if t == nil { 149 | return nil 150 | } 151 | // 整棵树的最右边节点就是值最大的节点 152 | if t.right == nil { 153 | return t 154 | } else { 155 | return t.right.maxNode() 156 | } 157 | } 158 | 159 | /* 160 | 左左情况:右旋 161 | * 162 | * 163 | * 164 | */ 165 | func (t *avlNode) llRotate() *avlNode { 166 | node := t.left 167 | t.left = node.right 168 | node.right = t 169 | 170 | node.h = max(node.left.height(), node.right.height()) + 1 171 | t.h = max(t.left.height(), t.right.height()) + 1 172 | return node 173 | } 174 | 175 | /* 176 | 右右情况:左旋 177 | * 178 | * 179 | * 180 | */ 181 | func (t *avlNode) rrRotate() *avlNode { 182 | node := t.right 183 | t.right = node.left 184 | node.left = t 185 | 186 | node.h = max(node.left.height(), node.right.height()) + 1 187 | t.h = max(t.left.height(), t.right.height()) + 1 188 | return node 189 | } 190 | 191 | /* 192 | 左右情况:先左旋 后右旋 193 | * 194 | * 195 | * 196 | */ 197 | func (t *avlNode) lrRotate() *avlNode { 198 | t.left = t.left.rrRotate() 199 | return t.llRotate() 200 | } 201 | 202 | /* 203 | 右左情况:先右旋 后左旋 204 | * 205 | * 206 | * 207 | */ 208 | func (t *avlNode) rlRotate() *avlNode { 209 | t.right = t.right.llRotate() 210 | return t.rrRotate() 211 | } 212 | 213 | func (t *avlNode) keepBalance(v int) *avlNode { 214 | // 左子树失衡 215 | if t.left.height()-t.right.height() == 2 { 216 | if v-t.left.value < 0 { 217 | // 当插入的节点在失衡节点的左子树的左子树中,直接右旋 218 | t = t.llRotate() 219 | } else { 220 | // 当插入的节点在失衡节点的左子树的右子树中,先左旋后右旋 221 | t = t.lrRotate() 222 | } 223 | } else if t.right.height()-t.left.height() == 2 { 224 | if t.right.right.height() > t.right.left.height() { 225 | // 当插入的节点在失衡节点的右子树的右子树中,直接左旋 226 | t = t.rrRotate() 227 | } else { 228 | // 当插入的节点在失衡节点的右子树的左子树中,先右旋后左旋 229 | t = t.rlRotate() 230 | } 231 | } 232 | // 调整树高度 233 | t.h = max(t.left.height(), t.right.height()) + 1 234 | return t 235 | } 236 | 237 | func (t *avlNode) height() int { 238 | if t != nil { 239 | return t.h 240 | } 241 | return -1 242 | } 243 | 244 | // 中序遍历按顺序获取所有值 245 | func appendValue(values []int, t *avlNode) []int { 246 | if t != nil { 247 | values = appendValue(values, t.left) 248 | values = append(values, t.value) 249 | values = appendValue(values, t.right) 250 | } 251 | return values 252 | } 253 | 254 | func (t *avlNode) values() []int { 255 | values := make([]int, 0) 256 | return appendValue(values, t) 257 | } 258 | -------------------------------------------------------------------------------- /avl_tree_test.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestAVLTree(t *testing.T) { 11 | tree := NewAVLTree() 12 | for i := 0; i < maxNum; i++ { 13 | tree.Insert(i) 14 | tree.Insert(maxNum + i) 15 | } 16 | assert.Equal(t, len(tree.AllValues()), maxNum*2) 17 | assert.True(t, assertSort(tree.AllValues())) 18 | assert.Equal(t, tree.GetMaxValue(), 2*maxNum-1) 19 | assert.Equal(t, tree.GetMinValue(), 0) 20 | assert.True(t, tree.Search(50)) 21 | assert.True(t, tree.Search(100)) 22 | assert.False(t, tree.Search(-10)) 23 | assert.False(t, tree.Delete(-10)) 24 | assert.True(t, tree.Delete(10)) 25 | assert.True(t, assertSort(tree.AllValues())) 26 | assert.Equal(t, len(tree.AllValues()), maxNum*2-1) 27 | } 28 | 29 | func TestAVLTreeRandom(t *testing.T) { 30 | tree := NewAVLTree() 31 | for i := 0; i < maxNum; i++ { 32 | tree.Insert(rand.Int()) 33 | } 34 | assert.Equal(t, len(tree.AllValues()), maxNum) 35 | assert.True(t, assertSort(tree.AllValues())) 36 | } 37 | 38 | func genAVL(n int) *AVLTree { 39 | t := NewAVLTree() 40 | for i := 0; i < n; i++ { 41 | t.Insert(rand.Int()) 42 | } 43 | return t 44 | } 45 | 46 | func BenchmarkAVLInsert10e1(b *testing.B) { genAVL(10e1) } 47 | func BenchmarkAVLInsert10e2(b *testing.B) { genAVL(10e2) } 48 | func BenchmarkAVLInsert10e3(b *testing.B) { genAVL(10e3) } 49 | func BenchmarkAVLInsert10e4(b *testing.B) { genAVL(10e4) } 50 | func BenchmarkAVLInsert10e5(b *testing.B) { genAVL(10e5) } 51 | 52 | var at = genAVL(10e5) 53 | 54 | func BenchmarkAVLSearch(b *testing.B) { at.Search(rand.Int()) } 55 | func BenchmarkAVLDelete(b *testing.B) { at.Delete(rand.Int()) } 56 | -------------------------------------------------------------------------------- /counter.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import "sort" 4 | 5 | type Counter struct { 6 | kv map[interface{}]int 7 | } 8 | 9 | type Item struct { 10 | k interface{} 11 | v int 12 | } 13 | 14 | func NewCounter() *Counter { 15 | return &Counter{make(map[interface{}]int)} 16 | } 17 | 18 | func (c *Counter) Add(keys ...interface{}) { 19 | for i := 0; i < len(keys); i++ { 20 | c.kv[keys[i]]++ 21 | } 22 | } 23 | 24 | func (c *Counter) Get(key interface{}) int { 25 | return c.kv[key] 26 | } 27 | 28 | func (c *Counter) GetAll() []Item { 29 | return c.sortMap() 30 | } 31 | 32 | func (c *Counter) Top(n int) []Item { 33 | sortItems := c.sortMap() 34 | if n > c.Len() || n < 0 { 35 | n = c.Len() 36 | } 37 | return sortItems[:n] 38 | } 39 | 40 | func (c *Counter) Delete(key interface{}) bool { 41 | if _, ok := c.kv[key]; ok { 42 | delete(c.kv, key) 43 | return true 44 | } 45 | return false 46 | } 47 | 48 | func (c *Counter) Len() int { 49 | return len(c.kv) 50 | } 51 | 52 | func (c *Counter) sortMap() []Item { 53 | var items []Item 54 | for k, v := range c.kv { 55 | items = append(items, Item{k, v}) 56 | } 57 | 58 | sort.Slice(items, func(i, j int) bool { 59 | return items[i].v > items[j].v 60 | }) 61 | return items 62 | } 63 | -------------------------------------------------------------------------------- /counter_test.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestEmptyCounter(t *testing.T) { 10 | c := NewCounter() 11 | assert.Equal(t, c.Len(), 0) 12 | assert.Equal(t, len(c.GetAll()), 0) 13 | assert.Equal(t, c.Get("anything"), 0) 14 | } 15 | 16 | func TestCounter(t *testing.T) { 17 | c := NewCounter() 18 | c.Add("a", "b", "c", "d", "a", "c", "c") 19 | assert.Equal(t, c.Top(2), []Item{{"c", 3}, {"a", 2}}) 20 | assert.Equal(t, c.Get("A"), 0) 21 | assert.Equal(t, c.Get("a"), 2) 22 | assert.Equal(t, c.Get("b"), 1) 23 | assert.Equal(t, c.Len(), 4) 24 | assert.Equal(t, len(c.Top(10)), c.Len()) 25 | assert.Equal(t, len(c.Top(-10)), c.Len()) 26 | c.Delete("a") 27 | assert.Equal(t, c.Get("a"), 0) 28 | } 29 | -------------------------------------------------------------------------------- /deque.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "container/list" 5 | "sync" 6 | ) 7 | 8 | type Deque struct { 9 | data *list.List 10 | mut *sync.RWMutex 11 | } 12 | 13 | func NewDeque() *Deque { 14 | return &Deque{data: list.New(), mut: new(sync.RWMutex)} 15 | } 16 | 17 | func (q *Deque) PutLeft(v interface{}) { 18 | defer q.mut.Unlock() 19 | q.mut.Lock() 20 | q.data.PushFront(v) 21 | } 22 | 23 | func (q *Deque) PutRight(v interface{}) { 24 | defer q.mut.Unlock() 25 | q.mut.Lock() 26 | q.data.PushBack(v) 27 | } 28 | 29 | func (q *Deque) GetLeft() (interface{}, bool) { 30 | defer q.mut.Unlock() 31 | q.mut.Lock() 32 | if q.data.Len() > 0 { 33 | iter := q.data.Front() 34 | v := iter.Value 35 | q.data.Remove(iter) 36 | return v, true 37 | } 38 | return nil, false 39 | } 40 | 41 | func (q *Deque) GetRight() (interface{}, bool) { 42 | defer q.mut.Unlock() 43 | q.mut.Lock() 44 | if q.data.Len() > 0 { 45 | iter := q.data.Back() 46 | v := iter.Value 47 | q.data.Remove(iter) 48 | return v, true 49 | } 50 | return nil, false 51 | } 52 | 53 | func (q *Deque) Qsize() int { 54 | defer q.mut.RUnlock() 55 | q.mut.RLock() 56 | return q.data.Len() 57 | } 58 | 59 | func (q *Deque) IsEmpty() bool { 60 | defer q.mut.RUnlock() 61 | q.mut.RLock() 62 | return !(q.data.Len() > 0) 63 | } 64 | -------------------------------------------------------------------------------- /deque_test.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestDeque(t *testing.T) { 10 | q := NewDeque() 11 | 12 | var item interface{} 13 | var ok bool 14 | 15 | for i := 0; i < nums; i++ { 16 | q.PutLeft(i) 17 | } 18 | assert.True(t, q.Qsize() == nums) 19 | 20 | for i := nums - 1; i >= 0; i-- { 21 | q.PutRight(i) 22 | } 23 | assert.True(t, q.Qsize() == nums*2) 24 | 25 | for i := 0; i < nums; i++ { 26 | item, ok = q.GetRight() 27 | assert.Equal(t, i, item) 28 | assert.Equal(t, ok, true) 29 | } 30 | for i := nums - 1; i >= 0; i-- { 31 | item, ok = q.GetLeft() 32 | assert.Equal(t, i, item) 33 | assert.Equal(t, ok, true) 34 | } 35 | assert.True(t, q.Qsize() == 0) 36 | 37 | item, ok = q.GetLeft() 38 | assert.Equal(t, nil, item) 39 | assert.Equal(t, ok, false) 40 | 41 | item, ok = q.GetRight() 42 | assert.Equal(t, nil, item) 43 | assert.Equal(t, ok, false) 44 | } 45 | -------------------------------------------------------------------------------- /lifo_queue.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "container/list" 5 | "sync" 6 | ) 7 | 8 | type LifoQueue struct { 9 | data *list.List 10 | mut *sync.RWMutex 11 | } 12 | 13 | func NewLifoQueue() *LifoQueue { 14 | return &LifoQueue{data: list.New(), mut: new(sync.RWMutex)} 15 | } 16 | 17 | func (q *LifoQueue) Put(v interface{}) { 18 | defer q.mut.Unlock() 19 | q.mut.Lock() 20 | q.data.PushFront(v) 21 | } 22 | 23 | func (q *LifoQueue) Get() (interface{}, bool) { 24 | defer q.mut.Unlock() 25 | q.mut.Lock() 26 | if q.data.Len() > 0 { 27 | iter := q.data.Front() 28 | v := iter.Value 29 | q.data.Remove(iter) 30 | return v, true 31 | } 32 | return nil, false 33 | } 34 | 35 | func (q *LifoQueue) Qsize() int { 36 | defer q.mut.RUnlock() 37 | q.mut.RLock() 38 | return q.data.Len() 39 | } 40 | 41 | func (q *LifoQueue) IsEmpty() bool { 42 | defer q.mut.RUnlock() 43 | q.mut.RLock() 44 | return !(q.data.Len() > 0) 45 | } 46 | -------------------------------------------------------------------------------- /lifo_queue_test.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestLifoQueue(t *testing.T) { 10 | q := NewLifoQueue() 11 | 12 | var item interface{} 13 | var ok bool 14 | 15 | for i := 0; i < nums; i++ { 16 | q.Put(i) 17 | } 18 | for i := nums - 1; i >= 0; i-- { 19 | item, ok = q.Get() 20 | assert.Equal(t, i, item.(int)) 21 | assert.Equal(t, ok, true) 22 | } 23 | 24 | item, ok = q.Get() 25 | assert.Equal(t, nil, item) 26 | assert.Equal(t, ok, false) 27 | 28 | item, ok = q.Get() 29 | assert.Equal(t, nil, item) 30 | assert.Equal(t, ok, false) 31 | } 32 | -------------------------------------------------------------------------------- /ordered_map.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | type linkedList struct { 4 | next, prev *linkedList 5 | key, value interface{} 6 | } 7 | 8 | type OrderedMap struct { 9 | head, tail, current *linkedList 10 | len int 11 | items map[interface{}]*linkedList 12 | } 13 | 14 | func NewOrderedMap() *OrderedMap { 15 | headNode := &linkedList{} 16 | tailNode := &linkedList{} 17 | headNode.next, tailNode.prev = tailNode, headNode 18 | return &OrderedMap{ 19 | head: headNode, 20 | tail: tailNode, 21 | current: headNode, 22 | len: 0, 23 | items: make(map[interface{}]*linkedList), 24 | } 25 | } 26 | 27 | func (om *OrderedMap) Set(key, value interface{}) { 28 | if _, ok := om.items[key]; ok { 29 | om.items[key].value = value 30 | return 31 | } 32 | newNode := &linkedList{prev: om.tail, next: nil, key: key, value: value} 33 | om.items[key] = newNode 34 | om.tail.next = newNode 35 | om.tail = newNode 36 | om.len++ 37 | } 38 | 39 | func (om *OrderedMap) Get(key interface{}) (interface{}, bool) { 40 | if v, ok := om.items[key]; ok { 41 | return v.value, ok 42 | } 43 | return nil, false 44 | } 45 | 46 | func (om *OrderedMap) Delete(key interface{}) bool { 47 | item, ok := om.items[key] 48 | if !ok { 49 | return ok 50 | } 51 | if item.next == nil { 52 | om.tail.prev, om.tail.next = om.tail, nil 53 | } else { 54 | item.prev.next, item.next.prev = item.next, item.prev 55 | } 56 | delete(om.items, key) 57 | item = nil 58 | om.len-- 59 | return true 60 | } 61 | 62 | func (om *OrderedMap) Iter() (interface{}, interface{}, bool) { 63 | c := om.current.next 64 | if c.next != nil { 65 | om.current = om.current.next 66 | return c.next.key, c.next.value, true 67 | } 68 | return nil, nil, false 69 | } 70 | 71 | func (om *OrderedMap) BackToHead() { 72 | om.current = om.head 73 | } 74 | 75 | func (om *OrderedMap) Len() int { 76 | return om.len 77 | } 78 | -------------------------------------------------------------------------------- /ordered_map_O_test.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "runtime" 5 | "sort" 6 | "testing" 7 | ) 8 | 9 | type tester interface { 10 | has(int) bool 11 | } 12 | 13 | type mapTester struct { 14 | m map[int]bool 15 | } 16 | 17 | func (m *mapTester) has(x int) bool { 18 | _, ok := m.m[x] 19 | return ok 20 | } 21 | 22 | type sliceTester struct { 23 | s []int 24 | } 25 | 26 | func (s *sliceTester) has(x int) bool { 27 | // sort.Search 使用的是二分查找 28 | i := sort.Search(len(s.s), func(i int) bool { return s.s[i] >= x }) 29 | // 未找到时 i 为 -1 30 | return i < len(s.s) && s.s[i] == x 31 | } 32 | 33 | func genMap(size int) tester { 34 | m := map[int]bool{} 35 | for i := 0; i < size; i++ { 36 | m[i] = true 37 | } 38 | return &mapTester{m: m} 39 | } 40 | 41 | func genSlice(size int) tester { 42 | s := make([]int, size) 43 | for i := 0; i < size; i++ { 44 | s[i] = i 45 | } 46 | return &sliceTester{s: s} 47 | } 48 | 49 | func benchmark(r tester, b *testing.B) { 50 | runtime.GC() 51 | b.ResetTimer() 52 | 53 | for i := 0; i < b.N; i++ { 54 | r.has(i) 55 | } 56 | } 57 | 58 | func BenchmarkMap10e1(b *testing.B) { benchmark(genMap(10e1), b) } 59 | func BenchmarkSlice10e1(b *testing.B) { benchmark(genSlice(10e1), b) } 60 | func BenchmarkMap10e2(b *testing.B) { benchmark(genMap(10e2), b) } 61 | func BenchmarkSlice10e2(b *testing.B) { benchmark(genSlice(10e2), b) } 62 | func BenchmarkMap10e3(b *testing.B) { benchmark(genMap(10e3), b) } 63 | func BenchmarkSlice10e3(b *testing.B) { benchmark(genSlice(10e3), b) } 64 | func BenchmarkMap10e4(b *testing.B) { benchmark(genMap(10e4), b) } 65 | func BenchmarkSlice10e4(b *testing.B) { benchmark(genSlice(10e4), b) } 66 | func BenchmarkMap10e5(b *testing.B) { benchmark(genMap(10e5), b) } 67 | func BenchmarkSlice10e5(b *testing.B) { benchmark(genSlice(10e5), b) } 68 | func BenchmarkMap10e6(b *testing.B) { benchmark(genMap(10e6), b) } 69 | func BenchmarkSlice10e6(b *testing.B) { benchmark(genSlice(10e6), b) } 70 | -------------------------------------------------------------------------------- /ordered_map_test.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/cevaris/ordered_map" 7 | ) 8 | 9 | const maxNum = 100 10 | 11 | func BenchmarkCollectionsSet(b *testing.B) { 12 | om := NewOrderedMap() 13 | for i := 0; i < b.N; i++ { 14 | om.Set(i, i) 15 | } 16 | } 17 | 18 | func BenchmarkCevarisSet(b *testing.B) { 19 | om := ordered_map.NewOrderedMap() 20 | for i := 0; i < b.N; i++ { 21 | om.Set(i, i) 22 | } 23 | } 24 | 25 | func BenchmarkCollectionsGet(b *testing.B) { 26 | om := NewOrderedMap() 27 | for i := 0; i < b.N; i++ { 28 | om.Set(i, i) 29 | } 30 | for i := 0; i < b.N; i++ { 31 | om.Get(i) 32 | } 33 | } 34 | 35 | func BenchmarkCevarisGet(b *testing.B) { 36 | om := ordered_map.NewOrderedMap() 37 | for i := 0; i < b.N; i++ { 38 | om.Set(i, i) 39 | } 40 | for i := 0; i < b.N; i++ { 41 | om.Get(i) 42 | } 43 | } 44 | 45 | func BenchmarkCollectionsIter(b *testing.B) { 46 | om := NewOrderedMap() 47 | for i := 0; i < b.N; i++ { 48 | om.Set(i, i) 49 | } 50 | for k, v, ok := om.Iter(); ok; k, v, ok = om.Iter() { 51 | _, _ = k, v 52 | } 53 | } 54 | 55 | func BenchmarkCevarisIter(b *testing.B) { 56 | om := ordered_map.NewOrderedMap() 57 | for i := 0; i < b.N; i++ { 58 | om.Set(i, i) 59 | } 60 | iter := om.IterFunc() 61 | for kv, ok := iter(); ok; kv, ok = iter() { 62 | _, _ = kv.Key, kv.Value 63 | } 64 | } 65 | 66 | func TestSet(t *testing.T) { 67 | om := NewOrderedMap() 68 | for i := 0; i < maxNum; i++ { 69 | om.Set(i, i+1) 70 | } 71 | if om.len != maxNum { 72 | t.Error() 73 | } 74 | } 75 | 76 | func TestGet(t *testing.T) { 77 | om := NewOrderedMap() 78 | for i := 0; i < maxNum; i++ { 79 | om.Set(i, i+1) 80 | } 81 | for i := 0; i < maxNum; i++ { 82 | v, ok := om.Get(i) 83 | if !ok || v.(int) != i+1 { 84 | t.Error() 85 | } 86 | } 87 | } 88 | 89 | func TestGUpdate(t *testing.T) { 90 | om := NewOrderedMap() 91 | for i := 0; i < maxNum; i++ { 92 | om.Set(i, i+1) 93 | } 94 | for i := maxNum - 1; i >= 0; i-- { 95 | om.Set(i, i-1) 96 | } 97 | for i := 0; i < maxNum; i++ { 98 | v, ok := om.Get(i) 99 | if !ok || v.(int) != i-1 { 100 | t.Error() 101 | } 102 | } 103 | } 104 | 105 | func TestIter(t *testing.T) { 106 | om := NewOrderedMap() 107 | for i := 0; i < maxNum; i++ { 108 | om.Set(i, i+1) 109 | } 110 | index := 0 111 | for k, v, ok := om.Iter(); ok; k, v, ok = om.Iter() { 112 | if k.(int) != index || v.(int) != index+1 || !ok { 113 | t.Error() 114 | } 115 | index++ 116 | } 117 | 118 | index = 0 119 | om.BackToHead() 120 | for k, v, ok := om.Iter(); ok; k, v, ok = om.Iter() { 121 | if k.(int) != index || v.(int) != index+1 || !ok { 122 | t.Error() 123 | } 124 | index++ 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /priority_queue.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "container/heap" 5 | "sync" 6 | ) 7 | 8 | type PriorityQueue struct { 9 | nodes []*PqNode 10 | mut *sync.RWMutex 11 | } 12 | 13 | // PriorityQueue Node 14 | type PqNode struct { 15 | Value string 16 | Priority, index int 17 | } 18 | 19 | func NewPriorityQueue() *PriorityQueue { 20 | pq := &PriorityQueue{mut: new(sync.RWMutex)} 21 | heap.Init(pq) 22 | return pq 23 | } 24 | 25 | func (pq *PriorityQueue) Put(v *PqNode) { 26 | defer pq.mut.Unlock() 27 | pq.mut.Lock() 28 | heap.Push(pq, v) 29 | } 30 | 31 | func (pq *PriorityQueue) Get() (interface{}, bool) { 32 | defer pq.mut.Unlock() 33 | pq.mut.Lock() 34 | if len(pq.nodes) > 0 { 35 | item := heap.Pop(pq) 36 | return item, true 37 | } 38 | return nil, false 39 | } 40 | 41 | func (pq PriorityQueue) Qsize() int { 42 | defer pq.mut.RUnlock() 43 | pq.mut.RLock() 44 | return len(pq.nodes) 45 | } 46 | 47 | func (pq *PriorityQueue) IsEmpty() bool { 48 | defer pq.mut.RUnlock() 49 | pq.mut.RLock() 50 | return !(len(pq.nodes) > 0) 51 | } 52 | 53 | // `Sort` interface Len() 54 | func (pq PriorityQueue) Len() int { 55 | return len(pq.nodes) 56 | } 57 | 58 | // `Sort` interface Less() 59 | func (pq PriorityQueue) Less(i, j int) bool { 60 | return pq.nodes[i].Priority > pq.nodes[j].Priority 61 | } 62 | 63 | // `Sort` interface Swap() 64 | func (pq PriorityQueue) Swap(i, j int) { 65 | pq.nodes[i], pq.nodes[j] = pq.nodes[j], pq.nodes[i] 66 | pq.nodes[i].index, pq.nodes[j].index = i, j 67 | } 68 | 69 | // `Heap` interface Push() 70 | func (pq *PriorityQueue) Push(v interface{}) { 71 | item := v.(*PqNode) 72 | item.index = len(pq.nodes) 73 | pq.nodes = append(pq.nodes, item) 74 | heap.Fix(pq, item.index) 75 | } 76 | 77 | // `Heap` interface Pop() 78 | func (pq *PriorityQueue) Pop() interface{} { 79 | old := *pq 80 | n := len(old.nodes) 81 | item := old.nodes[n-1] 82 | item.index = -1 83 | pq.nodes = old.nodes[0 : n-1] 84 | return item 85 | } 86 | -------------------------------------------------------------------------------- /priority_queue_test.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestPriorityQueue(t *testing.T) { 11 | q := NewPriorityQueue() 12 | 13 | var item1, item2 interface{} 14 | var ok bool 15 | 16 | for i := 0; i < nums; i++ { 17 | r := rand.Int() 18 | q.Put(&PqNode{Value: string(r), Priority: rand.Int()}) 19 | } 20 | 21 | for i := 0; i < nums/2; i++ { 22 | item1, ok = q.Get() 23 | item2, ok = q.Get() 24 | assert.True(t, item1.(*PqNode).Priority > item2.(*PqNode).Priority) 25 | assert.Equal(t, ok, true) 26 | } 27 | 28 | item1, ok = q.Get() 29 | assert.Equal(t, nil, item1) 30 | assert.Equal(t, ok, false) 31 | 32 | item1, ok = q.Get() 33 | assert.Equal(t, nil, item1) 34 | assert.Equal(t, ok, false) 35 | } 36 | -------------------------------------------------------------------------------- /queue.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "container/list" 5 | "sync" 6 | ) 7 | 8 | /* 9 | RWMutex 的使用主要事项 10 | 1. 读锁的时候无需等待读锁的结束 11 | 2. 读锁的时候要等待写锁的结束 12 | 3. 写锁的时候要等待读锁的结束 13 | 4. 写锁的时候要等待写锁的结束 14 | 15 | RWMutex 的四种操作方法 16 | RLock() //读锁定 17 | RUnlock() //读解锁 18 | Lock() //写锁定 19 | Unlock() //写解锁 20 | */ 21 | 22 | type Queue struct { 23 | data *list.List 24 | mut *sync.RWMutex 25 | } 26 | 27 | func NewQueue() *Queue { 28 | return &Queue{data: list.New(), mut: new(sync.RWMutex)} 29 | } 30 | 31 | func (q *Queue) Put(v interface{}) { 32 | defer q.mut.Unlock() 33 | q.mut.Lock() 34 | q.data.PushFront(v) 35 | } 36 | 37 | func (q *Queue) Get() (interface{}, bool) { 38 | defer q.mut.Unlock() 39 | q.mut.Lock() 40 | if q.data.Len() > 0 { 41 | iter := q.data.Back() 42 | v := iter.Value 43 | q.data.Remove(iter) 44 | return v, true 45 | } 46 | return nil, false 47 | } 48 | 49 | func (q *Queue) Qsize() int { 50 | defer q.mut.RUnlock() 51 | q.mut.RLock() 52 | return q.data.Len() 53 | } 54 | 55 | func (q *Queue) IsEmpty() bool { 56 | defer q.mut.RUnlock() 57 | q.mut.RLock() 58 | return !(q.data.Len() > 0) 59 | } 60 | -------------------------------------------------------------------------------- /queue_test.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | const nums = 1000 10 | 11 | func TestQueue(t *testing.T) { 12 | q := NewQueue() 13 | 14 | var item interface{} 15 | var ok bool 16 | 17 | for i := 0; i < nums; i++ { 18 | q.Put(i) 19 | } 20 | for i := 0; i < nums; i++ { 21 | item, ok = q.Get() 22 | assert.Equal(t, i, item.(int)) 23 | assert.Equal(t, ok, true) 24 | } 25 | 26 | item, ok = q.Get() 27 | assert.Equal(t, nil, item) 28 | assert.Equal(t, ok, false) 29 | 30 | item, ok = q.Get() 31 | assert.Equal(t, nil, item) 32 | assert.Equal(t, ok, false) 33 | } 34 | -------------------------------------------------------------------------------- /sort.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | // 冒泡排序:稳定 4 | // 平均 O(n^2) 最好 O(n) 最坏 O(n^2) 5 | // https://upload.wikimedia.org/wikipedia/commons/3/37/Bubble_sort_animation.gif 6 | func BubbleSort(items []int) { 7 | if len(items) < 2 { 8 | return 9 | } 10 | n := len(items) 11 | index := n 12 | for index > 0 { 13 | n = index 14 | index = 0 15 | for i := 1; i < n; i++ { 16 | if items[i-1] > items[i] { 17 | items[i-1], items[i] = items[i], items[i-1] 18 | // 表示 i 后面的数都已经排序好了 19 | // 如 3 2 4 5 6 7 8 9,在 3 和 2 交换之后 无需和后面进行交换 20 | // 就代表着 3 之后的数都是有序的,那后面的数就不用排了 21 | // 如果数组是基本有序的情况 性能会好很多 22 | index = i 23 | } 24 | } 25 | } 26 | } 27 | 28 | // 直接插入排序:稳定 29 | // 平均 O(n^2) 最好 O(n) 最差 O(n^2) 30 | // https://upload.wikimedia.org/wikipedia/commons/2/25/Insertion_sort_animation.gif 31 | func InsertionSort(items []int) { 32 | length := len(items) 33 | if length < 2 { 34 | return 35 | } 36 | 37 | for i := 1; i < length; i++ { 38 | // 如果右边的数小于左边的数 表示这个数的需要移动到左边 39 | if items[i] < items[i-1] { 40 | x := items[i] 41 | var j int 42 | // i-1 是原来的有序区 需要将 i 放入到原来的有序区中 形成一个更大的有序区 43 | // 2 3 4 5 1 6 44 | // j j+1 => j+1 = j 45 | // 2 3 4 1 5 6 46 | // j j+1 => j+1 = j 47 | // ... => j == -1 48 | // 1 2 3 4 5 6 => items[j+1] = x 49 | for j = i - 1; j >= 0 && items[j] > x; j-- { 50 | // 将比 x 大的数玩后面移动一位 51 | items[j+1] = items[j] 52 | } 53 | // 将 x 放入到正确的位置中 因为前面有 -- 操作,所以这里是 j+1 54 | items[j+1] = x 55 | } 56 | } 57 | } 58 | 59 | // 快速排序:不稳定 60 | // 平均 O(nlogn) 最好 O(nlogn) 最坏 O(n^2) 61 | // https://upload.wikimedia.org/wikipedia/commons/6/6a/Sorting_quicksort_anim.gif 62 | func QuickSort(items []int) { 63 | if len(items) < 2 { 64 | return 65 | } 66 | quickSort(items, 0, len(items)-1) 67 | } 68 | 69 | // 递归执行快速排序 70 | func quickSort(items []int, l, r int) { 71 | // 当左右相遇的时候 递归出口 72 | if l < r { 73 | i, j := l, r 74 | // 轴点 用于划分左右两部分 75 | pivot := items[i] 76 | // 左右相遇 完成一次划分排序操作 77 | for i < j { 78 | // 从右往左找 找到第一个小于 轴点 的数 79 | for i < j && items[j] >= pivot { 80 | j-- 81 | } 82 | // 如果找到的话 `填坑` 83 | if i < j { 84 | items[i] = items[j] 85 | i++ 86 | } 87 | // 从左往右找 找到第一个大于 轴点 的数 88 | for i < j && items[i] < pivot { 89 | i++ 90 | } 91 | // 如果找到的话 `填坑` 92 | if i < j { 93 | items[j] = items[i] 94 | j-- 95 | } 96 | } 97 | items[i] = pivot // 将轴点放到左右两边分界处 98 | quickSort(items, l, i-1) // 递归排序左半部分 99 | quickSort(items, i+1, r) // 递归排序由半部分 100 | } 101 | } 102 | 103 | // 希尔排序 不稳定 104 | // 平均 O(nlogn) 最好 O(n) 最坏 O(n^2) 105 | // https://upload.wikimedia.org/wikipedia/commons/d/d8/Sorting_shellsort_anim.gif 106 | func ShellSort(items []int) { 107 | length := len(items) 108 | if length < 2 { 109 | return 110 | } 111 | 112 | // 增量为 gap,每次更新为 gap /= 2 113 | for gap := length / 2; gap > 0; gap /= 2 { 114 | // 假设数组为 49, 38, 65, 55, 26, 13, 27, 49, 97, 4 长度为 10 115 | // 第一轮的时候 gap=5 分为 116 | // 13 49 - 27 38 - 49 65 - 97 55 - 4 26 117 | // 总共需要 5 次直接插入排序 排序完后的顺序 118 | // 13 49 - 27 38 - 49 65 - 55 97 - 4 26 119 | 120 | // 第二轮的时候 gap=2 分为 121 | // 13 27 49 55 4 - 49 38 65 97 26 122 | // 总共需要 2 次直接插入排序 排序完后的顺序 123 | // 4 13 27 49 55 - 26 38 49 65 97 124 | 125 | // 第三轮的时候 gap=1 分为 126 | // 4 13 27 49 55 26 38 49 65 97 127 | // 总共需要 1 次直接插入排序 排序完后的顺序 128 | // 4 13 26 27 38 49 49 55 65 97 129 | for j := gap; j < length; j++ { 130 | if items[j] < items[j-gap] { 131 | x := items[j] 132 | k := j - gap 133 | // items[k] 为`原先在左边的数` 可理解为原来的有序区 134 | // x 为即将放入到有序区的值,x < items[k],所以要把它往左边放 135 | for k >= 0 && items[k] > x { 136 | // 把 x 往左边放就意味着要把原先右边的值右移 137 | items[k+gap] = items[k] 138 | k -= gap 139 | } 140 | // 将 x 放入到正确的位置 141 | items[k+gap] = x 142 | } 143 | } 144 | } 145 | } 146 | 147 | // 堆排序:不稳定 148 | // 平均 O(nlogn) 最好 O(nlogn) 最坏 O(nlogn) 149 | // https://upload.wikimedia.org/wikipedia/commons/1/1b/Sorting_heapsort_anim.gif 150 | func HeapSort(items []int) { 151 | length := len(items) 152 | if length < 2 { 153 | return 154 | } 155 | 156 | // 初始化大顶堆 从最后一个父节点开始往前调整 157 | for i := length/2 - 1; i >= 0; i-- { 158 | adjustHeap(items, i, length-1) 159 | } 160 | 161 | // 进行堆排序 将最后一个叶子节点与根节点交换 162 | // 调整后的最后的的叶子节点即为最大值 适合求最大的 k 个数 163 | // 如 i > 0 => i > 3 则可以只算出最大的 3 个数 164 | for i := length - 1; i > 0; i-- { 165 | items[0], items[i] = items[i], items[0] 166 | adjustHeap(items, 0, i-1) 167 | } 168 | } 169 | 170 | // 调整堆 使其保持堆结构 171 | func adjustHeap(items []int, start, end int) { 172 | dad := start // 父节点 173 | son := dad*2 + 1 // 左子节点 174 | for son <= end { 175 | // son+1 为右子节点 如果右节点 > 左节点 son++ 取子节点中较大的一个 176 | if son+1 <= end && items[son] < items[son+1] { 177 | son++ 178 | } 179 | // 如果符合大顶堆结构 直接跳出 180 | if items[dad] > items[son] { 181 | return 182 | } 183 | // 讲父节点与子节点中较大的节点进行交换 184 | items[dad], items[son] = items[son], items[dad] 185 | // 在此修正子节点堆结构 186 | dad = son 187 | son = dad*2 + 1 188 | } 189 | } 190 | 191 | // 归并排序:稳定 192 | // 平均 O(nlogn) 最好 O(nlogn) 最差 O(nlogn) 193 | // https://upload.wikimedia.org/wikipedia/commons/c/c5/Merge_sort_animation2.gif 194 | func MergeSort(items []int) { 195 | length := len(items) 196 | if length < 2 { 197 | return 198 | } 199 | res := make([]int, length) 200 | mergeSort(items, 0, length-1, res) 201 | } 202 | 203 | // 递归执行归并排序及合并 204 | func mergeSort(items []int, first, last int, res []int) { 205 | // 递归出口 206 | if first < last { 207 | mid := (first + last) / 2 // 计算出切分左右部分的边界点 208 | mergeSort(items, first, mid, res) // 递归排序排序左半部分 209 | mergeSort(items, mid+1, last, res) // 递归排序排序右半部分 210 | mergeArray(items, first, mid, last, res) // 合并左右数组 211 | } 212 | } 213 | 214 | // 将两个有序数组按序合并为一个 215 | func mergeArray(items []int, first, mid, last int, res []int) { 216 | i, j := first, mid+1 217 | leftLen, rightLen := mid, last 218 | 219 | var k int 220 | 221 | // 左半部分 first mid 222 | // 右半部分 mid+1 last 223 | for i <= leftLen && j <= rightLen { 224 | // 如果左边的值大 则将左边的值放入到 res 中 225 | if items[i] <= items[j] { 226 | res[k] = items[i] 227 | k++ 228 | i++ 229 | // 如果右边的值大 则将右边的值放入到 res 中 230 | } else { 231 | res[k] = items[j] 232 | k++ 233 | j++ 234 | } 235 | } 236 | 237 | // 右边数组遍历结束了 将左边剩下的值放入 res 中 238 | for i <= leftLen { 239 | res[k] = items[i] 240 | k++ 241 | i++ 242 | } 243 | 244 | // 左边数组遍历结束了 将右边剩下的值放入 res 中 245 | for j <= rightLen { 246 | res[k] = items[j] 247 | k++ 248 | j++ 249 | } 250 | 251 | // 这里是重点 将合并后的数据放到`原 items`中 252 | // 也就说放入后从 first+i 到 k(leftLen+rightLen)中是有序的 253 | for i := 0; i < k; i++ { 254 | items[first+i] = res[i] 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /sort_test.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "math/rand" 5 | "sort" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | var maxCnt int = 10e3 12 | 13 | func yieldRandomArray(cnt int) []int { 14 | res := make([]int, cnt) 15 | for i := 0; i < cnt; i++ { 16 | res[i] = rand.Int() 17 | } 18 | return res 19 | } 20 | 21 | func assertSort(items []int) bool { 22 | for i := 0; i < len(items)-1; i++ { 23 | if items[i] > items[i+1] { 24 | return false 25 | } 26 | } 27 | return true 28 | } 29 | 30 | type StdItems struct { 31 | data []int 32 | } 33 | 34 | func (o StdItems) Less(i, j int) bool { 35 | return o.data[i] < o.data[j] 36 | } 37 | 38 | func (o StdItems) Swap(i, j int) { 39 | o.data[i], o.data[j] = o.data[j], o.data[i] 40 | } 41 | 42 | func (o StdItems) Len() int { 43 | return len(o.data) 44 | } 45 | 46 | func TestBubbleSort(t *testing.T) { 47 | items := yieldRandomArray(maxCnt) 48 | BubbleSort(items) 49 | assert.True(t, assertSort(items)) 50 | } 51 | 52 | func BenchmarkBubbleSort(b *testing.B) { 53 | for i := 0; i < b.N; i++ { 54 | items := yieldRandomArray(maxCnt) 55 | BubbleSort(items) 56 | } 57 | } 58 | 59 | func TestInsertionSort(t *testing.T) { 60 | items := yieldRandomArray(maxCnt) 61 | InsertionSort(items) 62 | assert.True(t, assertSort(items)) 63 | } 64 | 65 | func BenchmarkInsertionSort(b *testing.B) { 66 | for i := 0; i < b.N; i++ { 67 | items := yieldRandomArray(maxCnt) 68 | InsertionSort(items) 69 | } 70 | } 71 | 72 | func TestStdSort(t *testing.T) { 73 | items := StdItems{yieldRandomArray(maxCnt)} 74 | sort.Sort(items) 75 | assert.True(t, assertSort(items.data)) 76 | } 77 | 78 | func BenchmarkStdSort(b *testing.B) { 79 | for i := 0; i < b.N; i++ { 80 | sort.Sort(StdItems{yieldRandomArray(maxCnt)}) 81 | } 82 | } 83 | 84 | func TestQuickSort(t *testing.T) { 85 | items := yieldRandomArray(maxCnt) 86 | QuickSort(items) 87 | assert.True(t, assertSort(items)) 88 | } 89 | 90 | func BenchmarkQuickSort(b *testing.B) { 91 | for i := 0; i < b.N; i++ { 92 | items := yieldRandomArray(maxCnt) 93 | QuickSort(items) 94 | } 95 | } 96 | 97 | func TestShellSort(t *testing.T) { 98 | items := yieldRandomArray(maxCnt) 99 | ShellSort(items) 100 | assert.True(t, assertSort(items)) 101 | } 102 | 103 | func BenchmarkShellSort(b *testing.B) { 104 | for i := 0; i < b.N; i++ { 105 | items := yieldRandomArray(maxCnt) 106 | ShellSort(items) 107 | } 108 | } 109 | 110 | func TestHeapSort(t *testing.T) { 111 | items := yieldRandomArray(maxCnt) 112 | HeapSort(items) 113 | assert.True(t, assertSort(items)) 114 | } 115 | 116 | func BenchmarkHeapSort(b *testing.B) { 117 | for i := 0; i < b.N; i++ { 118 | items := yieldRandomArray(maxCnt) 119 | HeapSort(items) 120 | } 121 | } 122 | 123 | func TestMergeSort(t *testing.T) { 124 | items := yieldRandomArray(maxCnt) 125 | MergeSort(items) 126 | assert.True(t, assertSort(items)) 127 | } 128 | 129 | func BenchmarkMergeSort(b *testing.B) { 130 | for i := 0; i < b.N; i++ { 131 | items := yieldRandomArray(maxCnt) 132 | MergeSort(items) 133 | } 134 | } 135 | 136 | func TestStdSortWithoutInterface(t *testing.T) { 137 | items := yieldRandomArray(maxCnt) 138 | StdSortWithoutInterface(items) 139 | assert.True(t, assertSort(items)) 140 | } 141 | 142 | func BenchmarkStdSortWithoutInterface(b *testing.B) { 143 | for i := 0; i < b.N; i++ { 144 | items := yieldRandomArray(maxCnt) 145 | StdSortWithoutInterface(items) 146 | } 147 | } 148 | 149 | func TestStdSortWithGoroutine(t *testing.T) { 150 | items := yieldRandomArray(maxCnt) 151 | StdSortWithGoroutine(items) 152 | assert.True(t, assertSort(items)) 153 | } 154 | 155 | func BenchmarkStdSortWithGoroutine(b *testing.B) { 156 | for i := 0; i < b.N; i++ { 157 | items := yieldRandomArray(maxCnt) 158 | StdSortWithGoroutine(items) 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /std_sort.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | // Copyright 2009 The Go Authors. All rights reserved. 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | //go:generate go run genzfunc.go 8 | 9 | // Package sort provides primitives for sorting slices and user-defined 10 | // collections. 11 | 12 | // Insertion sort 13 | func insertionSort(data []int, a, b int) { 14 | for i := a + 1; i < b; i++ { 15 | for j := i; j > a && data[j] < data[j-1]; j-- { 16 | data[j], data[j-1] = data[j-1], data[j] 17 | } 18 | } 19 | } 20 | 21 | // siftDown implements the heap property on data[lo, hi). 22 | // first is an offset into the array where the root of the heap lies. 23 | func siftDown(data []int, lo, hi, first int) { 24 | root := lo 25 | for { 26 | child := 2*root + 1 27 | if child >= hi { 28 | break 29 | } 30 | if child+1 < hi && data[first+child] < data[first+child+1] { 31 | child++ 32 | } 33 | if !(data[first+root] < data[first+child]) { 34 | return 35 | } 36 | data[first+root], data[first+child] = data[first+child], data[first+root] 37 | root = child 38 | } 39 | } 40 | 41 | func heapSort(data []int, a, b int) { 42 | first := a 43 | lo := 0 44 | hi := b - a 45 | 46 | // Build heap with greatest element at top. 47 | for i := (hi - 1) / 2; i >= 0; i-- { 48 | siftDown(data, i, hi, first) 49 | } 50 | 51 | // Pop elements, largest first, into end of data. 52 | for i := hi - 1; i >= 0; i-- { 53 | data[first], data[first+i] = data[first+i], data[first] 54 | siftDown(data, lo, i, first) 55 | } 56 | } 57 | 58 | // Quicksort, loosely following Bentley and McIlroy, 59 | // ``Engineering a Sort Function,'' SP&E November 1993. 60 | 61 | // medianOfThree moves the median of the three values data[m0], data[m1], data[m2] into data[m1]. 62 | func medianOfThree(data []int, m1, m0, m2 int) { 63 | // sort 3 elements 64 | if data[m1] < data[m0] { 65 | data[m1], data[m0] = data[m0], data[m1] 66 | } 67 | // data[m0] <= data[m1] 68 | if data[m2] < data[m1] { 69 | data[m2], data[m1] = data[m1], data[m2] 70 | if data[m1] < data[m0] { 71 | data[m1], data[m0] = data[m0], data[m1] 72 | } 73 | } 74 | // now data[m0] <= data[m1] <= data[m2] 75 | } 76 | 77 | func doPivot(data []int, lo, hi int) (midlo, midhi int) { 78 | m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow. 79 | if hi-lo > 40 { 80 | // Tukey's ``Ninther,'' median of three medians of three. 81 | s := (hi - lo) / 8 82 | medianOfThree(data, lo, lo+s, lo+2*s) 83 | medianOfThree(data, m, m-s, m+s) 84 | medianOfThree(data, hi-1, hi-1-s, hi-1-2*s) 85 | } 86 | medianOfThree(data, lo, m, hi-1) 87 | 88 | // Invariants are: 89 | // data[lo] = pivot (set up by ChoosePivot) 90 | // data[lo < i < a] < pivot 91 | // data[a <= i < b] <= pivot 92 | // data[b <= i < c] unexamined 93 | // data[c <= i < hi-1] > pivot 94 | // data[hi-1] >= pivot 95 | pivot := lo 96 | a, c := lo+1, hi-1 97 | 98 | for ; a < c && data[a] < data[pivot]; a++ { 99 | } 100 | b := a 101 | for { 102 | for ; b < c && !(data[pivot] < data[b]); b++ { // data[b] <= pivot 103 | } 104 | for ; b < c && data[pivot] < data[c-1]; c-- { // data[c-1] > pivot 105 | } 106 | if b >= c { 107 | break 108 | } 109 | // data[b] > pivot; data[c-1] <= pivot 110 | data[b], data[c-1] = data[c-1], data[b] 111 | b++ 112 | c-- 113 | } 114 | // If hi-c<3 then there are duplicates (by property of median of nine). 115 | // Let be a bit more conservative, and set border to 5. 116 | protect := hi-c < 5 117 | if !protect && hi-c < (hi-lo)/4 { 118 | // Lets test some points for equality to pivot 119 | dups := 0 120 | if !(data[pivot] < data[hi-1]) { // data[hi-1] = pivot 121 | data[c], data[hi-1] = data[hi-1], data[c] 122 | c++ 123 | dups++ 124 | } 125 | if !(data[b-1] < data[pivot]) { // data[b-1] = pivot 126 | b-- 127 | dups++ 128 | } 129 | // m-lo = (hi-lo)/2 > 6 130 | // b-lo > (hi-lo)*3/4-1 > 8 131 | // ==> m < b ==> data[m] <= pivot 132 | if !(data[m] < data[pivot]) { // data[m] = pivot 133 | data[m], data[b-1] = data[b-1], data[m] 134 | b-- 135 | dups++ 136 | } 137 | // if at least 2 points are equal to pivot, assume skewed distribution 138 | protect = dups > 1 139 | } 140 | if protect { 141 | // Protect against a lot of duplicates 142 | // Add invariant: 143 | // data[a <= i < b] unexamined 144 | // data[b <= i < c] = pivot 145 | for { 146 | for ; a < b && !(data[b-1] < data[pivot]); b-- { // data[b] == pivot 147 | } 148 | for ; a < b && data[a] < data[pivot]; a++ { // data[a] < pivot 149 | } 150 | if a >= b { 151 | break 152 | } 153 | // data[a] == pivot; data[b-1] < pivot 154 | data[a], data[b-1] = data[b-1], data[a] 155 | a++ 156 | b-- 157 | } 158 | } 159 | // Swap pivot into middle 160 | data[pivot], data[b-1] = data[b-1], data[pivot] 161 | return b - 1, c 162 | } 163 | 164 | func qSort(data []int, a, b, maxDepth int) { 165 | for b-a > 12 { // Use ShellSort for slices <= 12 elements 166 | if maxDepth == 0 { 167 | heapSort(data, a, b) 168 | return 169 | } 170 | maxDepth-- 171 | mlo, mhi := doPivot(data, a, b) 172 | // Avoiding recursion on the larger subproblem guarantees 173 | // a stack depth of at most lg(b-a). 174 | if mlo-a < b-mhi { 175 | qSort(data, a, mlo, maxDepth) 176 | a = mhi // i.e., quickSort(data, mhi, b) 177 | } else { 178 | qSort(data, mhi, b, maxDepth) 179 | b = mlo // i.e., quickSort(data, a, mlo) 180 | } 181 | } 182 | if b-a > 1 { 183 | // Do ShellSort pass with gap 6 184 | // It could be written in this simplified form cause b-a <= 12 185 | for i := a + 6; i < b; i++ { 186 | if data[i] < data[i-6] { 187 | data[i], data[i-6] = data[i-6], data[i] 188 | } 189 | } 190 | insertionSort(data, a, b) 191 | } 192 | } 193 | 194 | // Sort sorts data. 195 | // It makes one call to data.Len to determine n, and O(n*log(n)) calls to 196 | // data.Less and data.Swap. The sort is not guaranteed to be stable. 197 | func stdSort(data []int, start, end int, ch chan bool) { 198 | n := len(data) 199 | mid := n / 2 200 | qSort(data, start, end, maxDepth(mid)) 201 | ch <- true 202 | } 203 | 204 | func StdSortWithoutInterface(data []int) { 205 | n := len(data) 206 | mid := n / 2 207 | qSort(data, 0, n, maxDepth(mid)) 208 | } 209 | 210 | func maxDepth(n int) int { 211 | var depth int 212 | for i := n; i > 0; i >>= 1 { 213 | depth++ 214 | } 215 | return depth * 2 216 | } 217 | 218 | func StdSortWithGoroutine(data []int) { 219 | n := len(data) 220 | mid := n / 2 221 | 222 | chs := make(chan bool, 2) 223 | 224 | go stdSort(data, 0, mid, chs) 225 | go stdSort(data, mid, n, chs) 226 | 227 | for i := 0; i < 2; i++ { 228 | <-chs 229 | } 230 | res := make([]int, n) 231 | mergeArray(data, 0, mid-1, n-1, res) 232 | } 233 | --------------------------------------------------------------------------------