前言
- 本文以最粗糙方式实现各种数据结构,一定有更优方式,读者可以自行扩展
- 如有错误之处欢迎批评指正
线性结构
数组
内存中顺序存储有序元素
特性:
- 随机访问高效,适合读多写少
- 插入删除需要移动元素,可能遇到扩容操作
操作 | 描述 | 时间复杂度 |
---|---|---|
查询 | 下标访问,随机读取 | O(1) |
更新 | 下标访问,随机读取,赋值 | O(1) |
插入 | 尾部插入 中间插入,后面元素右移 超范围插入,触发扩容copy | 综合O(N) O(1) O(N) O(N) |
删除 | 尾部删除 中间删除,后面元素左移 | 综合O(N) O(1) O(N) |
链表
内存中非连续非顺序随机存储
特性:
-
修改操作时间复杂度低,适合写多读少
-
不支持下标访问,查询最坏情况O(N),理论容量无限
操作 | 描述 | 时间复杂度 |
---|---|---|
查询 | 链式递归查找 | O(N) |
更新 | 链式递归查找,赋值 | 不考虑查找 O(1) |
插入 | 头部插入 中间插入 尾部插入 | 不考虑查找 O(1) O(1) O(1) |
删除 | 头部删除 中间删除 尾部删除 | 不考虑查找 O(1) O(1) O(1) |
逻辑结构
基于数组和链表结构派生线性存储结构
栈
特性
维护一个先入后出FILO的顺序线性结构,最早元素栈底,最新元素栈底
实现 | 出栈 | 入栈 |
---|---|---|
数组+index | 查询尾O(1) 尾删O(1) index– | 查询尾O(1) 尾插O(1) index++ |
链表+尾指针 | 查询尾O(1) 尾删O(1) 尾指针更新 | 查询尾O(1) 尾插O(1) 尾指针更新 |
应用
代替递归
简易实现
数组
type arrayStack struct {
data []interface{}
index int
}
func NewArrayStack() *arrayStack {
return &arrayStack{
data: make([]interface{},0),
index: -1,
}
}
func (a *arrayStack)Push(data interface{}) {
a.data = append(a.data, data)
a.index++
}
func (a *arrayStack)Pop() interface{} {
if len(a.data)==0{
return nil
}
r:=a.data[a.index]
if a.index == 0 {
a.data=make([]interface{},0)
}else {
a.data=a.data[:a.index]
}
a.index--
return r
}
func TestArrayStack(t *testing.T) {
node:=NewArrayStack()
for i:=0;i<10;i++{
node.Push(i)
}
for i:=0;i<11;i++{
fmt.Printf("%v \n",node.Pop())
}
}
/*
=== RUN TestArrayStack
9
8
7
6
5
4
3
2
1
0
<nil>
--- PASS: TestArrayStack (0.00s)
PASS
Process finished with exit code 0
*/
链表
type singleNode struct {
data interface{}
pre *singleNode
}
type linkStack struct {
data *singleNode
head *singleNode
}
func NewLinkStack() *linkStack {
return &linkStack{}
}
func (a *linkStack)Push(data interface{}) {
if a.data==nil{
a.data = &singleNode{
data: data,
pre: nil,
}
a.head = a.data
}else{
a.head = &singleNode{
data: data,
pre: a.head,
}
}
}
func (a *linkStack)Pop() interface{} {
if a.head==nil{
return nil
}
var r interface{}
r = a.head.data
if a.head.pre!=nil {
a.head = a.head.pre
}else{
a.head = nil
}
return r
}
func TestLinkStack(t *testing.T) {
node:=NewLinkStack()
for i:=0;i<10;i++{
node.Push(i)
}
for i:=0;i<11;i++{
fmt.Printf("%v \n",node.Pop())
}
}
/*
=== RUN TestLinkStack
9
8
7
6
5
4
3
2
1
0
<nil>
--- PASS: TestLinkStack (0.00s)
PASS
Process finished with exit code 0
*/
队列
特性
维护一个先入先出FIFO的顺序线性结构,最早元素队头,最新元素队尾
实现 | 出队 | 入队 |
---|---|---|
环形数组+队头index+队尾index | 查询队头O(1) 队头index– | 查询队尾O(1) 队尾index++ |
链表+头指针+尾指针 | 查询头O(1) 头删O(1) 头指针更新 | 查询尾O(1) 尾插O(1) 尾指针更新 |
应用
双端队列,结合栈和队列特性
优先队列,基于二叉堆实现
简易实现
数组
type arrayQueue struct {
data []interface{}
pre int
next int
}
func NewArrayQueue() *arrayQueue {
return &arrayQueue{
data: make([]interface{},1024),
pre: -1,
next:-1,
}
}
func (a *arrayQueue)Push(data interface{}) {
a.next = (a.next + 1) % 1024
a.data[a.next] = data
}
func (a *arrayQueue)Pop() interface{} {
if a.pre == a.next{
return nil
}
a.pre = (a.pre + 1) % 1024
return a.data[a.pre]
}
func TestNewArrayQueue(t *testing.T) {
m:=NewArrayQueue()
for i:=0;i<10;i++{
m.Push(i)
}
for i:=0;i<11;i++{
fmt.Printf("%v \n",m.Pop())
}
}
/*
=== RUN TestNewArrayQueue
0
1
2
3
4
5
6
7
8
9
<nil>
--- PASS: TestNewArrayQueue (0.00s)
PASS
Process finished with exit code 0
*/
链表
type linkQueue struct {
data *singleNode
tail *singleNode
}
func NewLinkQueue() *linkQueue {
return &linkQueue{}
}
func (a *linkQueue)Push(data interface{}) {
if a.data==nil{
a.data = &singleNode{
data: data,
next: nil,
}
a.tail = a.data
}else{
a.tail.next = &singleNode{
data: data,
next: nil,
}
a.tail = a.tail.next
}
}
func (a *linkQueue)Pop() interface{} {
if a.data==nil {
return nil
}
var r interface{}
r = a.data.data
if a.data.next!=nil {
a.data = a.data.next
}else {
a.data = nil
a.tail = nil
}
return r
}
func TestNewLinkQueue(t *testing.T) {
m:=NewLinkQueue()
for i:=0;i<10;i++{
m.Push(i)
}
for i:=0;i<11;i++{
fmt.Printf("%v \n",m.Pop())
}
}
/*
=== RUN TestNewLinkQueue
0
1
2
3
4
5
6
7
8
9
<nil>
--- PASS: TestNewLinkQueue (0.00s)
PASS
Process finished with exit code 0
*/
哈希表
维护一个键(Key)与值(Value)映射关系存储结构,也叫做散列表
- Capacity,当前hash表容量
- LoadFactor,当前负载因子默认0.75,衡量hash冲突概率的经验值
- Size,当前元素个数
底层是数组+链表结构,key进行hash,定位到数组下标。
- 如果hash冲突:
1.拉链法将key当前下标链式存储。
2.开放地址法将key往后寻找合适位置存储。
- 如果size>=Capacity*LoadFactor:
1.扩容底层数组
2.rehash重新放置key
操作 | 描述 | 时间复杂度 |
---|---|---|
查询 | hashkey,锁定数组index,有限长的链表查询 | 接近O(1) |
更新 | 查询,赋值 | 不考虑查找 接近O(1) |
插入 | hashkey,锁定数组index,链表尾插,可能触发扩容 | 不考虑扩容 接近O(1) |
删除 | hashkey,锁定数组index,有限长的链表查询,链表删除 | 不考虑查找 接近O(1) |
简易实现
type bucket struct {
key int
value interface{}
next *bucket
}
type m struct {
buckets []*bucket
}
func NewMap() *m {
return &m{buckets: make([]*bucket, 1024)}
}
func (m *m) Put(key int, v interface{}) {
index := key % 1024
if f := m.buckets[index]; f != nil {
p := f.next
for ; p != nil; p = p.next {
if p.key == key {
p.value = v
break
}
}
p.next = &bucket{
key: key,
value: v,
next: nil,
}
} else {
m.buckets[index] = &bucket{
key: key,
value: v,
next: nil,
}
}
}
func (m *m) Get(key int) interface{} {
index := key % 1024
if f := m.buckets[index]; f != nil {
if f.key == key {
return f.value
}
p := f.next
for ; p != nil; p = p.next {
if p.key == key {
return p.value
}
}
}
return nil
}
func TestNewMap(t *testing.T) {
m:=NewMap()
for i:=0;i<10240;i+=1000{
m.Put(i,i)
}
for i:=0;i<20000;i+=1000{
fmt.Printf("%v \n",m.Get(i))
}
}
/*
=== RUN TestNewMap
0
1000
2000
3000
4000
5000
6000
7000
8000
9000
10000
<nil>
<nil>
<nil>
<nil>
<nil>
<nil>
<nil>
<nil>
<nil>
--- PASS: TestNewMap (0.00s)
PASS
Process finished with exit code 0
*/
非线形结构
树
树是n(n>=0)个节点的有限集。
n = 0, 空树
1. 根节点:有且只有一个
2. 子树: n>2,根节点外其余节点可以划分m(m>0)个有限集,每个有限集又是一个树结构
3. 叶子节点:没有子树的节点
4. 父节点: 上一级节点
5. 子节点: 下一级节点
6. 兄弟节点: 同父节点的节点
二叉树
每个节点只有两个子节点的树,左边为左孩子,右边为右孩子,左边有限集为左子树,右边有限集为右子树
分类
- 满二叉树:所有非叶子节点都有左右孩子,所有叶子节点都在同一层级上。
- 完全二叉树:叶子节点层级上缺少右侧节点的满二叉树
- 稀疏二叉树:非上面两种二叉树,不适合用数组实现,浪费空间。
- 二叉搜索树(二叉排序树):左子树所有节点小于根节点,右子树所有节点大于根节点,左右子树也是二叉搜索树。
- 均匀 O(logN)
- 不均匀O(N),需要自平衡
遍历
- 前序遍历(深度优先):根->左->右
- 中序遍历(深度优先):左->根->右
- 后序遍历(深度优先):左->右->根
- 层序遍历(广度优先):层1->左->右->…->层n,前序+队列可以实现
// 遍历框架
func traverse(node){
if node == nil{
return
}
//前序位置
traverse(node.left)
//中序位置
traverse(node.rigtht)
//后序位置
}
// 层序遍历框架
func traverse(node){
queue:=newQueue()
queue.put(node)
for ;len(queue)!=nil;{
node = queue.pop()
// 层序遍历位置
if node.left!=nil {
queue.put(node.left)
}
if node.rigtht!=nil {
queue.put(node.rigtht)
}
}
}
简易实现
数组
package test
import "fmt"
type arrayTree []interface{}
func NewArrayTree(data []interface{}) *arrayTree {
a := &arrayTree{}
*a = append(*a, data...)
return a
}
func (a *arrayTree) traverse(index int) {
if index >len(*a)-1{
return
}
fmt.Printf("前序 %v \n",(*a)[index])
// 左节点
a.traverse(2*index+1)
fmt.Printf("中序 %v \n",(*a)[index])
// 右节点
a.traverse(2*index+2)
fmt.Printf("后序 %v \n",(*a)[index])
}
func (a *arrayTree) traverse2(index int) {
if index >len(*a)-1{
return
}
m:=NewLinkQueue()
m.Push(index)
for ;m.data!=nil;{
if v ,ok:=m.Pop().(int);ok{
fmt.Printf("层序遍历 %v \n",(*a)[v])
if v*2+1<len(*a){
m.Push(v*2+1)
}
if v*2+2<len(*a){
m.Push(v*2+2)
}
}
}
}
func TestNewArrayTree(t *testing.T) {
tree :=NewArrayTree([]interface{}{1,2,3,4,5,6,7,8,9})
tree.traverse(0)
tree.traverse2(0)
}
/*
=== RUN TestNewArrayTree
前序 1
前序 2
前序 4
前序 8
前序 9
前序 5
前序 3
前序 6
前序 7
--- PASS: TestNewArrayTree (0.00s)
PASS
Process finished with exit code 0
=== RUN TestNewArrayTree
中序 8
中序 4
中序 9
中序 2
中序 5
中序 1
中序 6
中序 3
中序 7
--- PASS: TestNewArrayTree (0.00s)
PASS
Process finished with exit code 0
=== RUN TestNewArrayTree
后序 8
后序 9
后序 4
后序 5
后序 2
后序 6
后序 7
后序 3
后序 1
--- PASS: TestNewArrayTree (0.00s)
PASS
Process finished with exit code 0
=== RUN TestNewArrayTree
层序遍历 1
层序遍历 2
层序遍历 3
层序遍历 4
层序遍历 5
层序遍历 6
层序遍历 7
层序遍历 8
层序遍历 9
--- PASS: TestNewArrayTree (0.00s)
PASS
Process finished with exit code 0
*/
链表
type LinkTree struct {
data interface{}
left *LinkTree
right *LinkTree
}
func NewLinkTree(data []interface{},index int) *LinkTree {
if len(data)==0 || index > len(data)-1{
return nil
}
node := &LinkTree{
data: data[index],
left: nil,
right: nil,
}
node.left = NewLinkTree(data,index*2+1)
node.right = NewLinkTree(data,index*2+2)
return node
}
func (a *LinkTree) traverse(node *LinkTree) {
if node == nil{
return
}
//fmt.Printf("前序 %v \n",node.data)
// 左节点
a.traverse(node.left)
//fmt.Printf("中序 %v \n",node.data)
// 右节点
a.traverse(node.right)
fmt.Printf("后序 %v \n",node.data)
}
func (a *LinkTree) traverse2(node *LinkTree) {
if node == nil{
return
}
m:=NewLinkQueue()
m.Push(node)
for ;m.data!=nil;{
if v ,ok:=m.Pop().(*LinkTree);ok{
fmt.Printf("层序遍历 %v \n",v.data)
if v.left!=nil{
m.Push(v.left)
}
if v.right!=nil{
m.Push(v.right)
}
}
}
}
func TestNewLinkTree(t *testing.T) {
tree :=NewLinkTree([]interface{}{1,2,3,4,5,6,7,8,9},0)
tree.traverse(tree)
tree.traverse2(tree)
}
/*
=== RUN TestNewLinkTree
前序 1
前序 2
前序 4
前序 8
前序 9
前序 5
前序 3
前序 6
前序 7
--- PASS: TestNewLinkTree (0.00s)
PASS
Process finished with exit code 0
*/
/*
=== RUN TestNewLinkTree
中序 8
中序 4
中序 9
中序 2
中序 5
中序 1
中序 6
中序 3
中序 7
--- PASS: TestNewLinkTree (0.00s)
PASS
Process finished with exit code 0
*/
/*
=== RUN TestNewLinkTree
后序 8
后序 9
后序 4
后序 5
后序 2
后序 6
后序 7
后序 3
后序 1
--- PASS: TestNewLinkTree (0.00s)
PASS
Process finished with exit code 0
=== RUN TestNewLinkTree
层序遍历 1
层序遍历 2
层序遍历 3
层序遍历 4
层序遍历 5
层序遍历 6
层序遍历 7
层序遍历 8
层序遍历 9
--- PASS: TestNewLinkTree (0.00s)
PASS
*/
二叉堆
本质上是完全二叉树,根节点是堆顶,根节点为最大值最大堆,反之最小堆。
操作 | 描述 | 时间复杂度 |
---|---|---|
插入 | 从最后位置插入,上浮(平均是层数一半) | O(log(N)) |
删除 | 从堆顶删除,下沉(平均是层数一半) | O(log(N)) |
构建 | 所有非叶子节点下沉 | O(N) |
简易实现
数组
package test
type arrayHeap []int
func NewArrayHeap(arr []int) *arrayHeap {
h := arrayHeap(arr)
for i := (len(arr) - 2) / 2; i >= 0; i-- {
h.adjustDown(i, len(arr))
}
return &h
}
func (h *arrayHeap) Push(v int) {
*h = append(*h, v)
h.adjustUp()
}
func (h *arrayHeap) Pop() int {
l := len([]int(*h))
r := (*h)[0]
if l > 1 {
(*h)[l-1], (*h)[0] = (*h)[0], (*h)[l-1]
*h = (*h)[:l-1]
h.adjustDown(0, l-1)
} else {
*h = make([]int, 0)
}
return r
}
func (h *arrayHeap) adjustUp() {
childIndex := len([]int(*h)) - 1
parentIndex := (childIndex - 1) / 2
for childIndex > 0 && (*h)[childIndex] < (*h)[parentIndex] {
(*h)[childIndex], (*h)[parentIndex] = (*h)[parentIndex], (*h)[childIndex]
childIndex = parentIndex
parentIndex = (childIndex - 1) / 2
}
}
func (h *arrayHeap) adjustDown(parentIndex int, l int) {
childIndex := 2*parentIndex + 1
for childIndex < l {
if childIndex+1 < l && (*h)[childIndex+1] < (*h)[childIndex] {
childIndex++
}
if (*h)[parentIndex] < (*h)[childIndex] {
break
}
(*h)[childIndex], (*h)[parentIndex] = (*h)[parentIndex], (*h)[childIndex]
parentIndex = childIndex
childIndex = 2*parentIndex + 1
}
}
func TestNewArrayHeap(t *testing.T) {
h := NewArrayHeap([]int{9,3,7,4,5,2,8,6,1})
fmt.Printf("%v \n",h)
i:=h.Pop()
fmt.Printf("%v \n",h)
h.Push(i)
fmt.Printf("%v \n",h)
}
/*
=== RUN TestNewArrayHeap
&[1 3 2 4 5 7 8 6 9]
&[2 3 7 4 5 9 8 6]
&[1 2 7 3 5 9 8 6 4]
--- PASS: TestNewArrayHeap (0.00s)
PASS
Process finished with exit code 0
*/
优先队列
最大堆/最小堆结合队列FIFO特性,保证出队列的都是最大值/最小值
图
图由有限顶点(vertex),有限边(edge)集合组成
1.有向图 边存在方向。 入节点数目 入度;出节点数目 出度
2.无向图 边不存在方向
3.带权图 边存在权重
4.稀疏/稠密图 e<nlog2n
实现 | 优点 | 缺点 |
---|---|---|
邻接矩阵 | 边和顶点兼顾,可以使用矩阵运算 | 浪费空间 |
邻接表 | 存储空间更优,更接近树结构 | 无法兼顾不存在边 |
遍历
- 深度优先遍历:首节点->相邻节点
- 广度优先遍历:首节点->所有相邻节点-> 下一层所有相邻节点 。深度遍历+队列实现
// 遍历框架
func DFS(*graph){
visited := make(map[*graph]bool)
dfs(*graph,visited)
}
// 深度优遍历
func dfs(*gragh,visited){
if gragh == nil{
return
}
visited[*graph] = true
// dfs遍历位置
for edge:=range graph.edges{
if !visited[*edge]{
dfs(*edge)
}
}
}
// 遍历框架
func BFS(*graph){
visited := make(map[*graph]bool)
bfs(*graph,visited)
}
// 广度优先遍历
func bfs(*graph,visited){
if gragh == nil{
return
}
visited[*graph] = true
queue:=newQueue()
queue.put(gragh)
for queue.isEmpty() {
node = queue.pop()
// bfs遍历位置
for edge:=range node.edges{
if !visited[*edge]{
visited[*edge] = true
queue.put(edge)
}
}
}
}
简易实现
邻接表
package test
import "fmt"
type graph struct {
key string
edges map[string]*graph
}
func NewGraph(ns []string,edges [][2]string) *graph {
g:=&graph{edges:make(map[string]*graph)}
for _,n :=range ns{
if _,ok:=g.edges[n];!ok{
g.edges[n] = &graph{
key: n,
edges: make(map[string]*graph),
}
}
}
for _,edge:=range edges{
g.edges[edge[0]].edges[edge[1]]=g.edges[edge[1]]
}
return g
}
// 遍历框架
func DFS(g *graph){
visited := make(map[*graph]bool)
g.dfs(visited)
}
// 深度优遍历
func (g *graph)dfs(visited map[*graph]bool){
if g == nil{
return
}
visited[g] = true
// dfs遍历位置
fmt.Printf("dfs %v \n",g.key)
for _,edge:=range g.edges{
if !visited[edge]{
edge.dfs(visited)
}
}
}
// 遍历框架
func BFS(g *graph){
visited := make(map[*graph]bool)
g.bfs(visited)
}
// 广度优先遍历
func (g *graph)bfs(visited map[*graph]bool){
if g == nil{
return
}
visited[g] = true
queue:=NewArrayQueue()
queue.Push(g)
for len(queue.data)!=0 {
if node,ok := queue.Pop().(*graph);ok{
// bfs遍历位置
fmt.Printf("bfs %v \n",node.key)
for _,edge:=range node.edges{
if !visited[edge]{
visited[edge] = true
queue.Push(edge)
}
}
}else{
break
}
}
}
func (g *graph)Print(m map[*graph]bool) {
if g.key !=""{
fmt.Printf("%v->",g.key)
}
for _, e := range g.edges {
if _,ok:=m[e];!ok{
e.Print(m)
m[e]=true
}
if g.key !=""{
fmt.Printf("%v->",e.key)
}
}
fmt.Printf("\n")
}
func TestNewGraph(t *testing.T) {
g:=NewGraph([]string{"1","2","3","4","5","6"},[][2]string{[2]string{"1","2"},[2]string{"1","3"},[2]string{"2","4"},[2]string{"2","5"}})
g.Print(make(map[*graph]bool))
DFS(g)
BFS(g)
}
/*
=== RUN TestNewGraph
5->
6->
1->2->4->
4->5->
2->3->
3->
dfs
dfs 2
dfs 4
dfs 5
dfs 3
dfs 6
dfs 1
bfs
bfs 1
bfs 2
bfs 3
bfs 4
bfs 5
bfs 6
--- PASS: TestNewGraph (0.00s)
PASS
Process finished with exit code 0
*/
邻接矩阵
type graph2 struct {
data map[string]map[string]bool
}
func NewGraph2(ns []string,edges [][2]string) *graph2 {
g:=&graph2{data:make(map[string]map[string]bool)}
for _,n :=range ns{
g.data[n]=make(map[string]bool)
}
for _,edge:=range edges{
g.data[edge[0]][edge[1]]=true
}
return g
}
// 遍历框架
func DFS2(g *graph2){
visited := make(map[string]bool)
for node, edges := range g.data {
if !visited[node] {
g.dfs(node, edges, visited)
}
}
}
// 深度优遍历
func (g *graph2)dfs(key string,edges map[string]bool,visited map[string]bool){
visited[key] = true
// dfs遍历位置
fmt.Printf("dfs %v \n",key)
for k,f:=range edges{
if !visited[k]&&f{
g.dfs(k,g.data[k],visited)
}
}
}
// 遍历框架
func BFS2(g *graph2){
visited := make(map[string]bool)
for node, _ := range g.data {
if !visited[node] {
g.bfs(node, visited)
}
}
}
// 广度优先遍历
func (g *graph2)bfs(key string,visited map[string]bool){
queue:=NewArrayQueue()
visited[key] = true
queue.Push(key)
for len(queue.data)!=0 {
if node,ok := queue.Pop().(string);ok{
// bfs遍历位置
fmt.Printf("bfs %v \n",node)
for k,f:=range g.data[key]{
if !visited[k]&&f{
visited[k] = true
queue.Push(k)
}
}
}else{
break
}
}
}
func (g *graph2)Print(m map[string]bool) {
for key, edges := range g.data {
fmt.Printf("%v: \n",key)
for e, f := range edges {
if _,ok:=m[e];!ok&&f{
fmt.Printf("%v - >%v \n",key,e)
m[e]=true
}
}
fmt.Printf("\n")
}
}
func TestNewGraph(t *testing.T) {
g:=NewGraph2([]string{"1","2","3","4","5","6"},[][2]string{[2]string{"1","2"},[2]string{"1","3"},[2]string{"2","4"},[2]string{"2","5"}})
g.Print(make(map[string]bool))
DFS2(g)
BFS2(g)
}
/*
=== RUN TestNewGraph
6:
1:
1 - >2
1 - >3
2:
2 - >4
2 - >5
3:
4:
5:
dfs 4
dfs 5
dfs 6
dfs 1
dfs 2
dfs 3
bfs 3
bfs 4
bfs 5
bfs 6
bfs 1
bfs 2
--- PASS: TestNewGraph (0.00s)
PASS
Process finished with exit code 0
*/