Golang算法与数据结构

Go 算法与数据结构

Go 基础知识点整理,包括切片、Map、闭包、方法和接口等核心概念

切片

切片的底层是数组构成,包含:头部指针len(长度)cap(容量)

无论怎么操作([:2][:0]),只要头部指针位置改变,cap 就会改变。

扩容机制

  • • < 1024:翻倍扩容
  • • > 1024:扩容 1.25 倍

为什么翻倍扩容?

扩容的时候会把所有数据(旧 + 新数据)拷贝,然后放入新数组。

+1 翻倍拷贝 过于频繁:

总拷贝次数 = 1 + 2 + 3 + ... + n

= n(n+1)/2 = O(n²)

翻倍扩容 则更优:

假设最终容量达到 n(即 2^k ≈ n),总拷贝次数:

T(n) = 1+2+4+8+...+2^k−1

= 2^k − 1 ≈ n − 1 = O(n)

过大扩容有可能会导致内存浪费,这在大于 1024 的时候只是扩容 1.25 倍可以看出。


Map

Go 语言采用的是哈希查找表,并且使用链表解决哈希冲突。

  • 哈希查找表:使用哈希函数将 Key 分配到不同的桶(数组的不同 index)。开销主要在哈希函数的计算以及数组的常数访问时间。
  • 碰撞问题:不同的 key 被哈希到了同一个 bucket。一般有两种应对方法:
    • 链表法:将一个 bucket 实现成链表,落在同一个 bucket 中的 key 都会插入这个链表
    • 开放地址法:碰撞发生后,通过一定的规律,在数组后面挑选"空位"放置新的 key
  • 搜索树法:一般采用自平衡搜索树,包括 AVL 树、红黑树。自平衡搜索树法的最差搜索效率是 O(logN),而哈希查找表最差是 O(N)。哈希查找表的平均查找效率是 O(1)。
    遍历自平衡搜索树,返回的 key 序列会按照从小到大的顺序;而哈希查找表则是乱序的。

闭包

理解
整理

方法

一类带特殊的接受者参数的函数。

type Vertex struct {X, Y float64}

带方法的函数:func (v Vertex) Abs() float64

普通函数:func Abs(v Vertex) float64

两者功能其实没有变化。

注意:"你只能为在同一包内定义的类型的接收者声明方法,而不能为其它包内定义的类型(包括 int 之类的内建类型)的接收者声明方法。"

type MyInt int        // 类型定义:创建全新类型,需要显式转换
type IntAlias = int   // 类型别名:只是别名,完全等价于原类型
                      // 不能为类型别名声明方法!
  • 指针接收者:可以修改接收者指向的值。由于方法经常需要修改它的接收者,指针接收者比值接收者更常用。
  • 值接收者:方法会对原始值的副本进行操作。方法必须用指针接收者来更改函数中声明的值。

方法与指针重定向

  • • 带指针参数的函数必须接受一个指针。而以指针为接收者的方法被调用时,接收者既能为值又能为指针。
  • 为什么选择值或者指针作为接受者?
    • ◦ 方法可以修改其接受者指向的值
    • ◦ 可以避免在每次调用方法时复制该值。若值的类型为大型结构体时,会更加高效

Interface

接口,是一种类型,一种抽象的类型。你能知道的仅仅是它能做什么,而不能知道它是什么。

  • • 实现接口的条件:一个类型如果拥有一个接口需要的所有方法,那么这个类型就实现了这个接口。
  • • 接口内嵌
  • • 接口值:一个具体的类型和该类型的值
  • sort.Interface
  • http.Handler 接口
  • error 接口

代码不是结论,更像我理解世界时留下的草稿。