'添加基础数据结构'
This commit is contained in:
parent
7e51c05d9a
commit
05d915754d
51
DataStructure/bitmap/bitmap.go
Normal file
51
DataStructure/bitmap/bitmap.go
Normal file
@ -0,0 +1,51 @@
|
||||
package bitmap
|
||||
|
||||
import "fmt"
|
||||
|
||||
//位图
|
||||
type BitMap struct {
|
||||
bits []byte
|
||||
max int
|
||||
}
|
||||
|
||||
//初始化一个BitMap
|
||||
//一个byte有8位,可代表8个数字,取余后加1为存放最大数所需的容量
|
||||
func NewBitMap(max int) *BitMap {
|
||||
bits := make([]byte, (max>>3)+1)
|
||||
return &BitMap{bits: bits, max: max}
|
||||
}
|
||||
|
||||
//添加一个数字到位图
|
||||
//计算添加数字在数组中的索引index,一个索引可以存放8个数字
|
||||
//计算存放到索引下的第几个位置,一共0-7个位置
|
||||
//原索引下的内容与1左移到指定位置后做或运算
|
||||
func (b *BitMap) Add(num uint) {
|
||||
index := num >> 3
|
||||
pos := num & 0x07
|
||||
b.bits[index] |= 1 << pos
|
||||
}
|
||||
|
||||
//判断一个数字是否在位图
|
||||
//找到数字所在的位置,然后做与运算
|
||||
func (b *BitMap) IsExist(num uint) bool {
|
||||
index := num >> 3
|
||||
pos := num & 0x07
|
||||
return b.bits[index]&(1<<pos) != 0
|
||||
}
|
||||
|
||||
//删除一个数字在位图
|
||||
//找到数字所在的位置取反,然后与索引下的数字做与运算
|
||||
func (b *BitMap) Remove(num uint) {
|
||||
index := num >> 3
|
||||
pos := num & 0x07
|
||||
b.bits[index] = b.bits[index] & ^(1 << pos)
|
||||
}
|
||||
|
||||
//位图的最大数字
|
||||
func (b *BitMap) Max() int {
|
||||
return b.max
|
||||
}
|
||||
|
||||
func (b *BitMap) String() string {
|
||||
return fmt.Sprint(b.bits)
|
||||
}
|
20
DataStructure/bitmap/bitmap_test.go
Normal file
20
DataStructure/bitmap/bitmap_test.go
Normal file
@ -0,0 +1,20 @@
|
||||
package bitmap
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewBitMap(t *testing.T) {
|
||||
max := 100
|
||||
b := NewBitMap(max)
|
||||
b.Add(13)
|
||||
b.Add(100)
|
||||
b.Add(0)
|
||||
assert.Equal(t, true, b.IsExist(13))
|
||||
assert.Equal(t, true, b.IsExist(0))
|
||||
assert.Equal(t, true, b.IsExist(100))
|
||||
b.Remove(13)
|
||||
assert.Equal(t, false, b.IsExist(13))
|
||||
assert.Equal(t, max, b.Max())
|
||||
}
|
69
DataStructure/graph/README.md
Normal file
69
DataStructure/graph/README.md
Normal file
@ -0,0 +1,69 @@
|
||||
## 图
|
||||
|
||||
[图的概念](http://data.biancheng.net/view/36.html)
|
||||
|
||||
[图的存储](https://blog.csdn.net/qq_22238021/article/details/78281939)
|
||||
|
||||
#### 网
|
||||
|
||||
带权的图就是网
|
||||
|
||||
### 图的存储结构
|
||||
|
||||
#### 邻接矩阵(顺序存储)
|
||||
|
||||
1. 顶点数组:存储顶点信息
|
||||
2. 边数组:存储顶点数组中2个顶点关系和权
|
||||
3. 图{顶点数组,边数组,顶点数,边数,图类型}
|
||||
|
||||
不适合:查找顶点的度,需要扫描整张边数组,效率低,对顶点的相关操作
|
||||
适合:对边依次进行处理的操作,
|
||||
|
||||
#### 邻接表(链式存储)
|
||||
|
||||
1. 表头结点:存储顶点信息(data)和第一个邻接点地址(firstarc),一般顺序存储在**一个数组中**
|
||||
2. 表中结点:存储表头结点在数组中的位置和下一个结点的指针,可存储权值
|
||||
3. 图{表头结点数组,顶点数,边数,图类型}
|
||||
|
||||
特点:
|
||||
|
||||
1. 出度为各自链表中的结点数,入度需要遍历整个表的结点,还有一种方法,求入度,建立一个逆邻接表
|
||||
2. 稀疏图存储时,比使用邻接矩阵节省空间
|
||||
|
||||
#### 十字链表(链式存储)
|
||||
|
||||
|
||||
1. 顶点结点:存储顶点信息(data) 一个弧头结点指针(firstin) 一个弧尾结点指针(firstout)
|
||||
2. 弧结点:tailvex 和 headvex 分别存储的是弧尾和弧头对应的顶点在数组中的位置下标; hlink 和 tlink 为指针域,分别指向弧头相同的下一个弧和弧尾相同的下一个弧; info 为指针域,存储的是该弧具有的相关信息,例如权值等
|
||||
3. 图{顶点结点数组,弧数,顶点数}
|
||||
|
||||
特点:
|
||||
1. 存储的是有向图或者有向网
|
||||
2. 求入度出度方便,入度为弧头的数量,出度为弧尾的数量
|
||||
3. 程序中构建链表对于每个新初始化的结点采用头插法进行插入
|
||||
|
||||
|
||||
#### 邻接多重表(链式存储)
|
||||
|
||||
邻接多重表可以看做是邻接表和十字链表的结合体
|
||||
|
||||
使用邻接表解决在无向图中删除某两个结点之间的边的操作时,由于表示边的结点分别处在两个顶点为头结点的链表中,所以需要都找到并删除,操作比较麻烦。处理类似这种操作,使用邻接多重表会更合适。
|
||||
|
||||
1. 表结点构成:
|
||||
|
||||
mark 为标志域,作用是标记某结点是否已经被操作过,例如在遍历各结点时, mark 域为 0 表示还未遍历;mark 域为 1 表示该结点遍历过;
|
||||
ivex 和 jvex 分别表示该结点表示的边两端的顶点在数组中的位置下标; ilink 指向下一条与 ivex 相关的边;
|
||||
jlink 指向下一条与 jvex 相关的边;
|
||||
info 指向与该边相关的信息。
|
||||
|
||||
2. 顶点结点构成:
|
||||
|
||||
data 为该顶点的数据域;
|
||||
firstedge 为指向第一条跟该顶点有关系的边。
|
||||
|
||||
#### 总结
|
||||
|
||||
1. 邻接表适用于所有的图结构,无论是有向图(网)还是无向图(网),存储结构较为简单,但是在存储一些问题时,例如计算某顶点的度,需要通过遍历的方式自己求得
|
||||
2. 十字链表适用于有向图(网)的存储,使用该方式存储的有向图,可以很容易计算出顶点的出度和入度,只需要知道对应链表中的结点个数即可
|
||||
3. 邻接多重表适用于无向图(网)的存储,该方式避免了使用邻接表存储无向图时出现的存储空间浪费的现象,同时相比邻接表存储无向图,更方便了某些边操作(遍历、删除等)的实现
|
||||
|
70
DataStructure/graph/directed_graph.go
Normal file
70
DataStructure/graph/directed_graph.go
Normal file
@ -0,0 +1,70 @@
|
||||
package graph
|
||||
|
||||
type DirGraph struct {
|
||||
graph
|
||||
}
|
||||
|
||||
//有向图
|
||||
func NewDirected() *DirGraph {
|
||||
return &DirGraph{
|
||||
graph{
|
||||
edgesCount: 0,
|
||||
edges: make(map[VertexId]map[VertexId]int),
|
||||
isDirected: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
//入度
|
||||
func (g *graph) GetPredecessors(vertex VertexId) VerticesIterable {
|
||||
iterator := func() <-chan VertexId {
|
||||
ch := make(chan VertexId)
|
||||
go func() {
|
||||
for VertexId, _ := range g.edges {
|
||||
if g.CheckEdge(VertexId, vertex) {
|
||||
ch <- VertexId
|
||||
}
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
return VerticesIterable(&VerticesIterableHelp{iter: iterator})
|
||||
}
|
||||
|
||||
//出度
|
||||
func (g *graph) GetSuccessors(vertex VertexId) VerticesIterable {
|
||||
iterator := func() <-chan VertexId {
|
||||
ch := make(chan VertexId)
|
||||
go func() {
|
||||
if connected, ok := g.edges[vertex]; ok {
|
||||
for VertexId, _ := range connected {
|
||||
if g.CheckEdge(vertex, VertexId) {
|
||||
ch <- VertexId
|
||||
}
|
||||
}
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
return VerticesIterable(&VerticesIterableHelp{iter: iterator})
|
||||
}
|
||||
|
||||
func (g *DirGraph) Reverse() *DirGraph {
|
||||
r := NewDirected()
|
||||
|
||||
vertices := g.VerticesIter()
|
||||
for vertex := range vertices {
|
||||
r.AddVertex(vertex)
|
||||
}
|
||||
|
||||
edges := g.EdgesIter()
|
||||
for edge := range edges {
|
||||
r.AddEdge(edge.To, edge.From, 1)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
182
DataStructure/graph/graph.go
Normal file
182
DataStructure/graph/graph.go
Normal file
@ -0,0 +1,182 @@
|
||||
package graph
|
||||
|
||||
import "errors"
|
||||
|
||||
const Infinity int = 65535 //无穷大
|
||||
|
||||
//非线程安全
|
||||
type VertexId uint
|
||||
|
||||
type Vertices []VertexId
|
||||
|
||||
type Edge struct {
|
||||
From VertexId
|
||||
To VertexId
|
||||
}
|
||||
|
||||
type graph struct {
|
||||
edges map[VertexId]map[VertexId]int
|
||||
edgesCount int
|
||||
isDirected bool //定向图
|
||||
}
|
||||
|
||||
type EdgesIterable interface {
|
||||
EdgesIter() <-chan Edge
|
||||
}
|
||||
|
||||
type VerticesIterable interface {
|
||||
VerticesIter() <-chan VertexId
|
||||
}
|
||||
|
||||
//边
|
||||
func (g *graph) EdgesIter() <-chan Edge {
|
||||
ch := make(chan Edge)
|
||||
go func() {
|
||||
for from, connectedVertices := range g.edges {
|
||||
for to, _ := range connectedVertices {
|
||||
if g.isDirected { //有向边
|
||||
ch <- Edge{from, to}
|
||||
} else {
|
||||
if from < to { //无向边,只输出一次
|
||||
ch <- Edge{from, to}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
//顶点
|
||||
func (g *graph) VerticesIter() <-chan VertexId {
|
||||
ch := make(chan VertexId)
|
||||
go func() {
|
||||
for vertex, _ := range g.edges {
|
||||
ch <- vertex
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
//检查顶点是否存在
|
||||
func (g *graph) CheckVertex(vertex VertexId) bool {
|
||||
_, exists := g.edges[vertex]
|
||||
return exists
|
||||
}
|
||||
|
||||
//增加顶点
|
||||
func (g *graph) AddVertex(vertex VertexId) error {
|
||||
if !g.CheckVertex(vertex) {
|
||||
g.edges[vertex] = make(map[VertexId]int)
|
||||
return nil
|
||||
} else {
|
||||
return errors.New("Vertex already exists")
|
||||
}
|
||||
}
|
||||
|
||||
//删除顶点
|
||||
func (g *graph) RemoveVertex(vertex VertexId) error {
|
||||
if !g.CheckVertex(vertex) {
|
||||
return errors.New("unknow vertex")
|
||||
}
|
||||
//先删除边
|
||||
for _to, _ := range g.edges[vertex] {
|
||||
g.RemoveEdge(vertex, _to)
|
||||
}
|
||||
delete(g.edges, vertex)
|
||||
for _, connectedVertices := range g.edges {
|
||||
delete(connectedVertices, vertex)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//统计顶点
|
||||
func (g *graph) VerticesCount() int {
|
||||
return len(g.edges)
|
||||
}
|
||||
|
||||
//判断边是否存在
|
||||
func (g *graph) CheckEdge(from, to VertexId) bool {
|
||||
if _, ok := g.edges[from][to]; ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//增加边,存在就修改权
|
||||
func (g *graph) AddEdge(from, to VertexId, weight int) error {
|
||||
//自身循环
|
||||
if from == to {
|
||||
return errors.New("cannot add self loop")
|
||||
}
|
||||
//不存在边
|
||||
if !g.CheckVertex(from) || !g.CheckVertex(to) {
|
||||
return errors.New("vertices not exist")
|
||||
}
|
||||
//判断边存在不
|
||||
if g.edges[from][to] > 0 {
|
||||
return errors.New("edge exist")
|
||||
}
|
||||
g.edges[from][to] = weight
|
||||
if !g.isDirected {
|
||||
g.edges[to][from] = weight
|
||||
}
|
||||
g.edgesCount++
|
||||
return nil
|
||||
}
|
||||
|
||||
//删除边
|
||||
func (g *graph) RemoveEdge(from, to VertexId) error {
|
||||
//判断边是否存在
|
||||
if !g.CheckEdge(from, to) {
|
||||
return errors.New("edge not exist")
|
||||
}
|
||||
//删除
|
||||
delete(g.edges[from], to)
|
||||
if !g.isDirected {
|
||||
delete(g.edges[to], from)
|
||||
}
|
||||
g.edgesCount--
|
||||
return nil
|
||||
}
|
||||
|
||||
//统计边
|
||||
func (g *graph) EdgesCount() int {
|
||||
return g.edgesCount
|
||||
}
|
||||
|
||||
//获取边权
|
||||
func (g *graph) GetEdgeWeight(from, to VertexId) int {
|
||||
if !g.CheckEdge(from, to) {
|
||||
return Infinity
|
||||
}
|
||||
return g.edges[from][to]
|
||||
}
|
||||
|
||||
//获取邻结点和权
|
||||
func (g *graph) GetNeighbours(vertex VertexId) VerticesIterable {
|
||||
iterator := func() <-chan VertexId {
|
||||
ch := make(chan VertexId)
|
||||
go func() {
|
||||
if connected, ok := g.edges[vertex]; ok {
|
||||
for vid, _ := range connected {
|
||||
ch <- vid
|
||||
}
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
return VerticesIterable(&VerticesIterableHelp{iter: iterator})
|
||||
}
|
||||
|
||||
//帮助获取
|
||||
type VerticesIterableHelp struct {
|
||||
iter func() <-chan VertexId
|
||||
}
|
||||
|
||||
func (v *VerticesIterableHelp) VerticesIter() <-chan VertexId {
|
||||
return v.iter()
|
||||
}
|
92
DataStructure/graph/graph_test.go
Normal file
92
DataStructure/graph/graph_test.go
Normal file
@ -0,0 +1,92 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewUndirected(t *testing.T) {
|
||||
g := NewUndirected()
|
||||
//增加顶点
|
||||
for i := 0; i < 10; i++ {
|
||||
g.AddVertex(VertexId(i))
|
||||
}
|
||||
assert.Equal(t, 10, g.VerticesCount())
|
||||
//增加边
|
||||
for i := 0; i < 10; i++ {
|
||||
g.AddEdge(VertexId(i), VertexId(i%2), 1)
|
||||
}
|
||||
//
|
||||
assert.Equal(t, true, g.CheckEdge(2, 0))
|
||||
assert.Equal(t, false, g.CheckEdge(2, 1))
|
||||
assert.Equal(t, 1, g.GetEdgeWeight(2, 0))
|
||||
//删除顶点
|
||||
assert.NoError(t, g.RemoveVertex(VertexId(2)))
|
||||
assert.Equal(t, false, g.CheckVertex(VertexId(2)))
|
||||
assert.Equal(t, false, g.CheckEdge(2, 0))
|
||||
//增加边,存在修改
|
||||
assert.NoError(t, g.AddEdge(3, 0, 1))
|
||||
assert.Equal(t, true, g.CheckEdge(3, 0))
|
||||
//删除边
|
||||
assert.NoError(t, g.RemoveEdge(3, 0))
|
||||
assert.Equal(t, false, g.CheckEdge(3, 0))
|
||||
|
||||
//统计边
|
||||
c := g.EdgesIter()
|
||||
countEdge := 0
|
||||
for _ = range c {
|
||||
countEdge++
|
||||
}
|
||||
assert.Equal(t, countEdge, g.EdgesCount())
|
||||
|
||||
}
|
||||
|
||||
func TestNewDirected(t *testing.T) {
|
||||
g := NewDirected()
|
||||
//增加顶点
|
||||
for i := 0; i < 10; i++ {
|
||||
g.AddVertex(VertexId(i))
|
||||
}
|
||||
assert.Equal(t, 10, g.VerticesCount())
|
||||
//增加边
|
||||
for i := 0; i < 10; i++ {
|
||||
g.AddEdge(VertexId(i), VertexId(i%2), 1)
|
||||
}
|
||||
//
|
||||
assert.Equal(t, true, g.CheckEdge(2, 0))
|
||||
assert.Equal(t, false, g.CheckEdge(2, 1))
|
||||
assert.Equal(t, 1, g.GetEdgeWeight(2, 0))
|
||||
//删除顶点
|
||||
assert.NoError(t, g.RemoveVertex(VertexId(2)))
|
||||
assert.Equal(t, false, g.CheckVertex(VertexId(2)))
|
||||
assert.Equal(t, false, g.CheckEdge(2, 0))
|
||||
//增加边,存在修改
|
||||
assert.NoError(t, g.AddEdge(3, 0, 1))
|
||||
assert.Equal(t, true, g.CheckEdge(3, 0))
|
||||
//删除边
|
||||
assert.NoError(t, g.RemoveEdge(3, 0))
|
||||
assert.Equal(t, false, g.CheckEdge(3, 0))
|
||||
|
||||
//统计边
|
||||
c := g.EdgesIter()
|
||||
countEdge := 0
|
||||
for _ = range c {
|
||||
countEdge++
|
||||
}
|
||||
assert.Equal(t, countEdge, g.EdgesCount())
|
||||
//查看
|
||||
//for p := range g.EdgesIter() {
|
||||
// t.Log(p)
|
||||
//}
|
||||
//入度
|
||||
gp := g.GetPredecessors(VertexId(1)).VerticesIter()
|
||||
for p := range gp {
|
||||
if p != 3 && p != 5 && p != 7 && p != 9 {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
||||
for p := range g.GetSuccessors(VertexId(4)).VerticesIter() {
|
||||
assert.Equal(t, VertexId(0), p)
|
||||
}
|
||||
}
|
17
DataStructure/graph/undirected_graph.go
Normal file
17
DataStructure/graph/undirected_graph.go
Normal file
@ -0,0 +1,17 @@
|
||||
package graph
|
||||
|
||||
//无向图
|
||||
|
||||
type UnGraph struct {
|
||||
graph
|
||||
}
|
||||
|
||||
func NewUndirected() *UnGraph {
|
||||
return &UnGraph{
|
||||
graph{
|
||||
edgesCount: 0,
|
||||
edges: make(map[VertexId]map[VertexId]int),
|
||||
isDirected: false,
|
||||
},
|
||||
}
|
||||
}
|
123
DataStructure/heap/heap.go
Normal file
123
DataStructure/heap/heap.go
Normal file
@ -0,0 +1,123 @@
|
||||
package heap
|
||||
|
||||
import "sync"
|
||||
|
||||
/*
|
||||
*二叉堆
|
||||
*假设"第一个元素"在数组中的索引为 0 的话,则父节点和子节点的位置关系如下:
|
||||
*(01) 索引为i的左孩子的索引是 (2*i+1);
|
||||
*(02) 索引为i的左孩子的索引是 (2*i+2);
|
||||
*(03) 索引为i的父结点的索引是 floor((i-1)/2);
|
||||
*/
|
||||
type Item interface {
|
||||
Less(than Item) bool
|
||||
}
|
||||
|
||||
type Int int
|
||||
|
||||
func (x Int) Less(than Item) bool {
|
||||
return x < than.(Int)
|
||||
}
|
||||
|
||||
type Heap struct {
|
||||
sync.Mutex
|
||||
data []Item
|
||||
min bool
|
||||
}
|
||||
|
||||
func New() *Heap {
|
||||
return &Heap{
|
||||
data: make([]Item, 0),
|
||||
}
|
||||
}
|
||||
|
||||
//最小堆
|
||||
func NewMin() *Heap {
|
||||
return &Heap{
|
||||
data: make([]Item, 0),
|
||||
min: true,
|
||||
}
|
||||
}
|
||||
|
||||
//最大堆
|
||||
func NewMax() *Heap {
|
||||
return &Heap{
|
||||
data: make([]Item, 0),
|
||||
min: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Heap) IsEmpty() bool {
|
||||
return len(h.data) == 0
|
||||
}
|
||||
|
||||
func (h *Heap) Len() int {
|
||||
return len(h.data)
|
||||
}
|
||||
func (h *Heap) Get(n int) Item {
|
||||
return h.data[n]
|
||||
}
|
||||
|
||||
//根据生成最大堆还是最小堆,返回比较
|
||||
func (h *Heap) Less(a, b Item) bool {
|
||||
if h.min {
|
||||
return a.Less(b)
|
||||
} else {
|
||||
return b.Less(a)
|
||||
}
|
||||
}
|
||||
|
||||
//插入元素
|
||||
func (h *Heap) Insert(n Item) {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
h.data = append(h.data, n)
|
||||
h.siftUp()
|
||||
return
|
||||
}
|
||||
|
||||
//取出元素
|
||||
func (h *Heap) Extract() (el Item) {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
if h.Len() == 0 {
|
||||
return
|
||||
}
|
||||
el = h.data[0]
|
||||
last := h.data[h.Len()-1]
|
||||
if h.Len() == 1 {
|
||||
h.data = nil
|
||||
return
|
||||
}
|
||||
h.data = append([]Item{last}, h.data[1:h.Len()-1]...)
|
||||
h.siftDown()
|
||||
return
|
||||
}
|
||||
|
||||
//上升
|
||||
func (h *Heap) siftUp() {
|
||||
for i, parent := h.Len()-1, h.Len()-1; i > 0; i = parent {
|
||||
parent = i >> 1 //位移,向下取整
|
||||
if h.Less(h.Get(i), h.Get(parent)) {
|
||||
h.data[parent], h.data[i] = h.data[i], h.data[parent]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//下降
|
||||
func (h *Heap) siftDown() {
|
||||
for i, child := 0, 1; i < h.Len() && i<<1+1 < h.Len(); i = child {
|
||||
child = i<<1 + 1
|
||||
if child+1 <= h.Len()-1 && h.Less(h.Get(child+1), h.Get(child)) {
|
||||
child++
|
||||
}
|
||||
|
||||
if h.Less(h.Get(i), h.Get(child)) {
|
||||
break
|
||||
}
|
||||
|
||||
h.data[i], h.data[child] = h.data[child], h.data[i]
|
||||
}
|
||||
}
|
34
DataStructure/heap/heap_test.go
Normal file
34
DataStructure/heap/heap_test.go
Normal file
@ -0,0 +1,34 @@
|
||||
package heap
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewMax(t *testing.T) {
|
||||
mh := NewMax()
|
||||
mh.Insert(Int(5))
|
||||
mh.Insert(Int(7))
|
||||
mh.Insert(Int(2))
|
||||
mh.Insert(Int(6))
|
||||
mh.Insert(Int(3))
|
||||
assert.Equal(t, Int(7), mh.Extract())
|
||||
assert.Equal(t, Int(6), mh.Extract())
|
||||
mh.Insert(Int(12))
|
||||
assert.Equal(t, 4, mh.Len())
|
||||
assert.Equal(t, Int(12), mh.Extract())
|
||||
}
|
||||
|
||||
func TestNewMin(t *testing.T) {
|
||||
mh := NewMin()
|
||||
mh.Insert(Int(5))
|
||||
mh.Insert(Int(7))
|
||||
mh.Insert(Int(2))
|
||||
mh.Insert(Int(6))
|
||||
mh.Insert(Int(3))
|
||||
assert.Equal(t, Int(2), mh.Extract())
|
||||
assert.Equal(t, Int(3), mh.Extract())
|
||||
mh.Insert(Int(12))
|
||||
assert.Equal(t, 4, mh.Len())
|
||||
assert.Equal(t, Int(5), mh.Extract())
|
||||
}
|
75
DataStructure/priority_queue/priority_queue.go
Normal file
75
DataStructure/priority_queue/priority_queue.go
Normal file
@ -0,0 +1,75 @@
|
||||
package priority_queue
|
||||
|
||||
import (
|
||||
"algorithms-go/DataStructure/heap"
|
||||
"algorithms-go/DataStructure/queue"
|
||||
)
|
||||
|
||||
//优先队列与队列区别:按照优先级自动排序
|
||||
//抽象的数据类型,具有队列的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的
|
||||
|
||||
type Item struct {
|
||||
Value interface{}
|
||||
Priority int
|
||||
}
|
||||
|
||||
func NewItem(value interface{}, priority int) (i *Item) {
|
||||
return &Item{
|
||||
Value: value,
|
||||
Priority: priority,
|
||||
}
|
||||
}
|
||||
|
||||
func (x Item) Less(than heap.Item) bool {
|
||||
return x.Priority < than.(Item).Priority
|
||||
}
|
||||
|
||||
type PQ struct {
|
||||
data heap.Heap
|
||||
}
|
||||
|
||||
func NewMax() (q *PQ) {
|
||||
return &PQ{
|
||||
data: *heap.NewMax(),
|
||||
}
|
||||
}
|
||||
|
||||
func NewMin() (q *PQ) {
|
||||
return &PQ{
|
||||
data: *heap.NewMin(),
|
||||
}
|
||||
}
|
||||
|
||||
func (pq *PQ) Len() int {
|
||||
return pq.data.Len()
|
||||
}
|
||||
|
||||
func (pq *PQ) Insert(el Item) {
|
||||
pq.data.Insert(heap.Item(el))
|
||||
}
|
||||
|
||||
func (pq *PQ) Extract() (el Item) {
|
||||
return pq.data.Extract().(Item)
|
||||
}
|
||||
|
||||
func (pq *PQ) ChangePriority(val interface{}, priority int) {
|
||||
var storage = queue.New()
|
||||
|
||||
popped := pq.Extract()
|
||||
|
||||
for val != popped.Value {
|
||||
if pq.Len() == 0 {
|
||||
panic("Item not found")
|
||||
}
|
||||
|
||||
storage.Push(popped)
|
||||
popped = pq.Extract()
|
||||
}
|
||||
|
||||
popped.Priority = priority
|
||||
pq.data.Insert(popped)
|
||||
|
||||
for storage.Len() > 0 {
|
||||
pq.data.Insert(storage.Shift().(heap.Item))
|
||||
}
|
||||
}
|
82
DataStructure/priority_queue/priority_queue_test.go
Normal file
82
DataStructure/priority_queue/priority_queue_test.go
Normal file
@ -0,0 +1,82 @@
|
||||
package priority_queue
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMaxPriorityQueue(t *testing.T) {
|
||||
h := NewMax()
|
||||
|
||||
h.Insert(*NewItem(8, 10))
|
||||
h.Insert(*NewItem(7, 11))
|
||||
h.Insert(*NewItem(6, 12))
|
||||
h.Insert(*NewItem(3, 13))
|
||||
h.Insert(*NewItem(1, 14))
|
||||
h.Insert(*NewItem(0, 15))
|
||||
h.Insert(*NewItem(2, 16))
|
||||
h.Insert(*NewItem(4, 17))
|
||||
h.Insert(*NewItem(9, 18))
|
||||
h.Insert(*NewItem(5, 19))
|
||||
|
||||
sorted := make([]Item, 0)
|
||||
for h.Len() > 0 {
|
||||
sorted = append(sorted, h.Extract())
|
||||
}
|
||||
|
||||
for i := 0; i < len(sorted)-2; i++ {
|
||||
if sorted[i].Priority < sorted[i+1].Priority {
|
||||
fmt.Println(sorted)
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinPriorityQueue(t *testing.T) {
|
||||
h := NewMin()
|
||||
|
||||
h.Insert(*NewItem(8, 10))
|
||||
h.Insert(*NewItem(7, 11))
|
||||
h.Insert(*NewItem(6, 12))
|
||||
h.Insert(*NewItem(3, 13))
|
||||
h.Insert(*NewItem(1, 14))
|
||||
h.Insert(*NewItem(0, 15))
|
||||
h.Insert(*NewItem(2, 16))
|
||||
h.Insert(*NewItem(4, 17))
|
||||
h.Insert(*NewItem(9, 18))
|
||||
h.Insert(*NewItem(5, 19))
|
||||
|
||||
sorted := make([]Item, 0)
|
||||
for h.Len() > 0 {
|
||||
sorted = append(sorted, h.Extract())
|
||||
}
|
||||
|
||||
for i := 0; i < len(sorted)-2; i++ {
|
||||
if sorted[i].Priority > sorted[i+1].Priority {
|
||||
fmt.Println(sorted)
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestChangePriority(t *testing.T) {
|
||||
h := NewMax()
|
||||
|
||||
h.Insert(*NewItem(8, 10))
|
||||
h.Insert(*NewItem(7, 11))
|
||||
h.Insert(*NewItem(6, 12))
|
||||
h.Insert(*NewItem(3, 13))
|
||||
h.Insert(*NewItem(1, 14))
|
||||
h.Insert(*NewItem(0, 15))
|
||||
h.Insert(*NewItem(2, 16))
|
||||
h.Insert(*NewItem(4, 17))
|
||||
h.Insert(*NewItem(9, 18))
|
||||
h.Insert(*NewItem(5, 19))
|
||||
|
||||
h.ChangePriority(8, 66)
|
||||
popped := h.Extract()
|
||||
|
||||
if popped.Value != 8 {
|
||||
t.Error()
|
||||
}
|
||||
}
|
29
DataStructure/queue/list_queue.go
Normal file
29
DataStructure/queue/list_queue.go
Normal file
@ -0,0 +1,29 @@
|
||||
package queue
|
||||
|
||||
import "container/list"
|
||||
|
||||
//使用go标准库的list实现
|
||||
type ListQueue struct {
|
||||
l *list.List
|
||||
}
|
||||
|
||||
func NewListQueue() *ListQueue {
|
||||
l := list.New()
|
||||
return &ListQueue{l}
|
||||
}
|
||||
|
||||
func (lq *ListQueue) Shift() (el interface{}) {
|
||||
e := lq.l.Front()
|
||||
el = e.Value
|
||||
lq.l.Remove(e)
|
||||
return
|
||||
}
|
||||
|
||||
func (lq *ListQueue) Push(el interface{}) {
|
||||
lq.l.PushBack(el)
|
||||
return
|
||||
}
|
||||
|
||||
func (lq *ListQueue) Len() int {
|
||||
return lq.l.Len()
|
||||
}
|
45
DataStructure/queue/queue.go
Normal file
45
DataStructure/queue/queue.go
Normal file
@ -0,0 +1,45 @@
|
||||
package queue
|
||||
|
||||
import "sync"
|
||||
|
||||
type Queue struct {
|
||||
queue []interface{}
|
||||
len int
|
||||
lock *sync.Mutex
|
||||
}
|
||||
|
||||
func New() *Queue {
|
||||
q := &Queue{}
|
||||
q.queue = make([]interface{}, 0)
|
||||
q.len = 0
|
||||
q.lock = new(sync.Mutex)
|
||||
return q
|
||||
}
|
||||
|
||||
func (q *Queue) Len() int {
|
||||
return q.len
|
||||
}
|
||||
|
||||
func (q *Queue) isEmpty() bool {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
return q.len == 0
|
||||
}
|
||||
|
||||
func (q *Queue) Shift() (el interface{}) {
|
||||
el, q.queue = q.queue[0], q.queue[1:]
|
||||
q.len--
|
||||
return
|
||||
}
|
||||
|
||||
func (q *Queue) Push(el interface{}) {
|
||||
q.queue = append(q.queue, el)
|
||||
q.len++
|
||||
return
|
||||
}
|
||||
|
||||
func (q *Queue) Peek() (el interface{}) {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
return q.queue[0]
|
||||
}
|
32
DataStructure/queue/queue_test.go
Normal file
32
DataStructure/queue/queue_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
package queue
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
queue := New()
|
||||
assert.Equal(t, true, queue.isEmpty())
|
||||
queue.Push(1)
|
||||
queue.Push(2)
|
||||
queue.Push(3)
|
||||
assert.Equal(t, 3, queue.Len())
|
||||
assert.Equal(t, 1, queue.Shift().(int))
|
||||
assert.Equal(t, 2, queue.Shift().(int))
|
||||
assert.Equal(t, 3, queue.Peek().(int))
|
||||
assert.Equal(t, 3, queue.Shift().(int))
|
||||
|
||||
}
|
||||
|
||||
func TestNewListQueue(t *testing.T) {
|
||||
queue := NewListQueue()
|
||||
queue.Push(1)
|
||||
queue.Push(2)
|
||||
queue.Push(3)
|
||||
assert.Equal(t, 3, queue.Len())
|
||||
assert.Equal(t, 1, queue.Shift().(int))
|
||||
assert.Equal(t, 2, queue.Shift().(int))
|
||||
assert.Equal(t, 3, queue.Shift().(int))
|
||||
assert.Equal(t, 0, queue.Len())
|
||||
}
|
60
DataStructure/stack/stack_array.go
Normal file
60
DataStructure/stack/stack_array.go
Normal file
@ -0,0 +1,60 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const ARRAY_SIZE = 10
|
||||
|
||||
//数组实现
|
||||
type Stack struct {
|
||||
data [ARRAY_SIZE]int
|
||||
top int
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func New() *Stack {
|
||||
return &Stack{}
|
||||
}
|
||||
|
||||
func (s *Stack) Len() int {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
return s.top
|
||||
}
|
||||
|
||||
func (s *Stack) IsEmpty() bool {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
return s.top == 0
|
||||
}
|
||||
|
||||
func (s *Stack) Push(i int) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
if s.top == ARRAY_SIZE {
|
||||
return errors.New("栈已满")
|
||||
}
|
||||
s.data[s.top] = i
|
||||
s.top++
|
||||
return nil
|
||||
}
|
||||
func (s *Stack) Pop() (int, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
if s.top == 0 {
|
||||
return 0, errors.New("栈空")
|
||||
}
|
||||
s.top--
|
||||
return s.data[s.top], nil
|
||||
}
|
||||
|
||||
func (s *Stack) Peek() (int, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
if s.top == 0 {
|
||||
return 0, errors.New("栈空")
|
||||
}
|
||||
return s.data[s.top-1], nil
|
||||
}
|
35
DataStructure/stack/stack_array_test.go
Normal file
35
DataStructure/stack/stack_array_test.go
Normal file
@ -0,0 +1,35 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
s := New()
|
||||
for i := 0; i < 10; i++ {
|
||||
err := s.Push(i)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
err := s.Push(11)
|
||||
assert.Error(t, err)
|
||||
for i := 9; i >= 0; i-- {
|
||||
k, _ := s.Pop()
|
||||
assert.Equal(t, i, k)
|
||||
}
|
||||
_, err = s.Pop()
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestNewListStack(t *testing.T) {
|
||||
s := NewListStack()
|
||||
for i := 0; i < 10; i++ {
|
||||
s.Push(i)
|
||||
}
|
||||
for i := 9; i >= 0; i-- {
|
||||
k, _ := s.Pop()
|
||||
assert.Equal(t, i, k)
|
||||
}
|
||||
_, err := s.Pop()
|
||||
assert.Error(t, err)
|
||||
}
|
54
DataStructure/stack/stack_list.go
Normal file
54
DataStructure/stack/stack_list.go
Normal file
@ -0,0 +1,54 @@
|
||||
package stack
|
||||
|
||||
import "errors"
|
||||
|
||||
//使用链表实现栈
|
||||
//以下是使用单向链表,使用头插法实现,也可以使用标准库的双向链表实现
|
||||
//节点,需要动态申请内存空间
|
||||
type Node struct {
|
||||
data int
|
||||
next *Node
|
||||
}
|
||||
|
||||
//栈
|
||||
type ListStack struct {
|
||||
top *Node
|
||||
}
|
||||
|
||||
func NewListStack() *ListStack {
|
||||
return &ListStack{}
|
||||
}
|
||||
|
||||
//是否为空
|
||||
func (ls *ListStack) IsEmpty() bool {
|
||||
return ls.top == nil
|
||||
}
|
||||
|
||||
//入栈,头插法
|
||||
func (ls *ListStack) Push(i int) {
|
||||
//新建节点
|
||||
n := &Node{i, nil}
|
||||
if ls.top != nil {
|
||||
n.next = ls.top
|
||||
}
|
||||
ls.top = n
|
||||
}
|
||||
|
||||
//出栈,从头弹出
|
||||
func (ls *ListStack) Pop() (int, error) {
|
||||
if ls.top == nil {
|
||||
return 0, errors.New("栈空")
|
||||
}
|
||||
i := ls.top.data
|
||||
ls.top = ls.top.next
|
||||
return i, nil
|
||||
|
||||
}
|
||||
|
||||
//查看
|
||||
func (ls *ListStack) Peek() (int, error) {
|
||||
if ls.top == nil {
|
||||
return 0, errors.New("栈空")
|
||||
}
|
||||
return ls.top.data, nil
|
||||
}
|
54
DataStructure/tree/binaryTree/README.md
Normal file
54
DataStructure/tree/binaryTree/README.md
Normal file
@ -0,0 +1,54 @@
|
||||
## 树
|
||||
|
||||
### 三种基本的存储引擎
|
||||
|
||||
- 哈希存储引擎
|
||||
是哈希表的持久化实现,支持增、删、改以及随机读取操作,**但不支持顺序扫描**,对应的存储系统为key-value存储系统。对于key-value的插入以及查询,哈希表的复杂度都是O(1),明显比树的操作O(n)快,如果不需要有序的遍历数据,哈希表就是your Mr.Right
|
||||
- B树存储引擎
|
||||
是B树的持久化实现,不仅支持单条记录的增、删、读、改操作,还支持顺序扫描(B+树的叶子节点之间的指针),对应的存储系统就是关系数据库(Mysql等)。
|
||||
- LSM树(Log-Structured Merge Tree)存储引擎
|
||||
和B树存储引擎一样,同样支持增、删、读、改、顺序扫描操作。而且通过批量存储技术规避磁盘随机写入问题。**当然凡事有利有弊,LSM树和B+树相比,LSM树牺牲了部分读性能,用来大幅提高写性能。**
|
||||
|
||||
### BTree和B+Tree
|
||||
|
||||
[详解](https://www.cnblogs.com/vianzhang/p/7922426.html)
|
||||
|
||||
B树是为磁盘存储而专门设计的一类平衡搜索树
|
||||
|
||||
B+树中的B代表平衡(balance)
|
||||
B+树最大的性能问题是为了读取有序性,插入会产生大量的随机IO,因为磁盘寻道速度慢
|
||||
|
||||
- 典型应用
|
||||
|
||||
|
||||
|
||||
### 平衡二叉树(AVL Tree)
|
||||
|
||||
### LSM树 与 B+ Tree
|
||||
|
||||
产品:HBase, Cassandra, LevelDB, SQLite,甚至在mangodb3.0中也带了一个可选的LSM引擎
|
||||
|
||||
[LSM树由来、设计思想以及应用到HBase的索引](https://www.cnblogs.com/yanghuahui/p/3483754.html)
|
||||
[概念](https://www.cnblogs.com/bonelee/p/6244810.html)
|
||||
|
||||
设计背景:顺序读写磁盘(不管是SATA还是SSD)快于随机读写主存,而且快至少三个数量级。这说明我们要避免随机读写,最好设计成顺序读写
|
||||
|
||||
LSM树的设计思想非常朴素:将对数据的修改增量保持在内存中,达到指定的大小限制后将这些修改操作批量写入磁盘,,不过读取的时候稍微麻烦,需要合并磁盘中历史数据和内存中最近修改操作,所以写入性能大大提升,读取时可能需要先看是否命中内存,否则需要访问较多的磁盘文件。极端的说,基于LSM树实现的HBase的写性能比Mysql高了一个数量级,读性能低了一个数量级
|
||||
|
||||
它的原理是把一颗大树拆分成N棵小树, 它首先写入到内存中(内存没有寻道速度的问题,随机写的性能得到大幅提升),在内存中构建一颗有序小树,随着小树越来越大,内存的小树会flush到磁盘上。当读时,由于不知道数据在哪棵小树上,因此必须遍历所有的小树,但在每颗小树内部数据是有序的
|
||||
|
||||
以上就是LSM树最本质的原理,有了原理,再看具体的技术就很简单了。
|
||||
|
||||
1. 首先说说为什么要有WAL(Write Ahead Log),很简单,因为数据是先写到内存中,如果断电,内存中的数据会丢失,因此为了保护内存中的数据,需要在磁盘上先记录logfile,当内存中的数据flush到磁盘上时,就可以抛弃相应的Logfile。
|
||||
|
||||
2. 什么是memstore, storefile?很简单,上面说过,LSM树就是一堆小树,在内存中的小树即memstore,每次flush,内存中的memstore变成磁盘上一个新的storefile。
|
||||
|
||||
3. 为什么会有compact?很简单,随着小树越来越多,读的性能会越来越差,数据也有冗余,因此需要在适当的时候,对磁盘中的小树进行merge,多棵小树变成一颗大树。
|
||||
|
||||
#### LSM tree 操作流程如下:
|
||||
|
||||
1. 数据写入和更新时首先写入位于内存里的数据结构。为了避免数据丢失也会先写到 WAL 文件中。
|
||||
2. 内存里的数据结构会定时或者达到固定大小会刷到磁盘。这些磁盘上的文件不会被修改。
|
||||
3. 随着磁盘上积累的文件越来越多,会定时的进行合并操作,消除冗余数据,减少文件数量。
|
||||
|
||||
|
169
DataStructure/tree/binaryTree/bst.go
Normal file
169
DataStructure/tree/binaryTree/bst.go
Normal file
@ -0,0 +1,169 @@
|
||||
package binaryTree
|
||||
|
||||
//二叉树
|
||||
//二叉树是每个节点最多有两个子树的树结构
|
||||
type ElementType int //节点数据
|
||||
//结点
|
||||
type Node struct {
|
||||
Value ElementType
|
||||
Parent *Node
|
||||
Left *Node
|
||||
Right *Node
|
||||
}
|
||||
|
||||
//创建节点
|
||||
func NewNode(v ElementType) *Node {
|
||||
return &Node{Value: v}
|
||||
}
|
||||
|
||||
/*
|
||||
*节点比较
|
||||
* n>m:1 n<m:-1 n=m:0
|
||||
*/
|
||||
func (n *Node) Compare(m *Node) int {
|
||||
if n.Value < m.Value {
|
||||
return -1
|
||||
} else if n.Value > m.Value {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
//树
|
||||
type Tree struct {
|
||||
Head *Node
|
||||
Size int
|
||||
}
|
||||
|
||||
//创建树
|
||||
func NewTree(n *Node) *Tree {
|
||||
if n == nil {
|
||||
return &Tree{}
|
||||
}
|
||||
return &Tree{Head: n, Size: 1}
|
||||
}
|
||||
|
||||
//插入,相同的节点值,放到右子树
|
||||
func (t *Tree) Insert(i ElementType) {
|
||||
n := NewNode(i) //创建节点
|
||||
if t.Head == nil { //判断树的根节点
|
||||
t.Head = n
|
||||
t.Size++
|
||||
return
|
||||
}
|
||||
|
||||
h := t.Head
|
||||
|
||||
for {
|
||||
if n.Compare(h) == -1 { //小于parent,到左子节点
|
||||
if h.Left == nil { //无左子节点
|
||||
h.Left = n
|
||||
n.Parent = h
|
||||
break
|
||||
} else {
|
||||
h = h.Left
|
||||
}
|
||||
} else { //大于parent
|
||||
if h.Right == nil {
|
||||
h.Right = n
|
||||
n.Parent = h
|
||||
break
|
||||
} else {
|
||||
h = h.Right
|
||||
}
|
||||
}
|
||||
}
|
||||
t.Size++
|
||||
}
|
||||
|
||||
//搜索
|
||||
func (t *Tree) Search(i ElementType) *Node {
|
||||
h := t.Head
|
||||
n := NewNode(i)
|
||||
for h != nil {
|
||||
switch h.Compare(n) {
|
||||
case -1:
|
||||
h = h.Right
|
||||
case 1:
|
||||
h = h.Left
|
||||
case 0:
|
||||
return h
|
||||
default:
|
||||
panic("Node not found")
|
||||
}
|
||||
}
|
||||
panic("Node not found")
|
||||
}
|
||||
|
||||
//删除
|
||||
func (t *Tree) Delete(i ElementType) bool {
|
||||
var parent *Node
|
||||
|
||||
h := t.Head
|
||||
n := NewNode(i)
|
||||
|
||||
for h != nil {
|
||||
switch n.Compare(h) {
|
||||
case -1:
|
||||
parent = h
|
||||
h = h.Left
|
||||
case 1:
|
||||
parent = h
|
||||
h = h.Right
|
||||
case 0:
|
||||
if h.Left != nil {
|
||||
right := h.Right
|
||||
h.Value = h.Left.Value
|
||||
h.Left = h.Left.Left
|
||||
h.Right = h.Left.Right
|
||||
|
||||
if right != nil {
|
||||
subTree := &Tree{Head: h}
|
||||
IterOnTree(right, func(n *Node) {
|
||||
subTree.Insert(n.Value)
|
||||
})
|
||||
}
|
||||
t.Size--
|
||||
return true
|
||||
}
|
||||
|
||||
if h.Right != nil {
|
||||
h.Value = h.Right.Value
|
||||
h.Left = h.Right.Left
|
||||
h.Right = h.Right.Right
|
||||
|
||||
t.Size--
|
||||
return true
|
||||
}
|
||||
|
||||
if parent == nil {
|
||||
t.Head = nil
|
||||
t.Size--
|
||||
return true
|
||||
}
|
||||
|
||||
if parent.Left == n {
|
||||
parent.Left = nil
|
||||
} else {
|
||||
parent.Right = nil
|
||||
}
|
||||
t.Size--
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IterOnTree(n *Node, f func(*Node)) bool {
|
||||
if n == nil {
|
||||
return true
|
||||
}
|
||||
if !IterOnTree(n.Left, f) {
|
||||
return false
|
||||
}
|
||||
|
||||
f(n)
|
||||
|
||||
return IterOnTree(n.Right, f)
|
||||
}
|
29
DataStructure/tree/binaryTree/bst_test.go
Normal file
29
DataStructure/tree/binaryTree/bst_test.go
Normal file
@ -0,0 +1,29 @@
|
||||
package binaryTree
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNode_Compare(t *testing.T) {
|
||||
n := NewNode(1)
|
||||
m := NewNode(2)
|
||||
k := NewNode(1)
|
||||
|
||||
assert.Equal(t, -1, n.Compare(m))
|
||||
assert.Equal(t, 0, n.Compare(k))
|
||||
assert.Equal(t, 1, m.Compare(k))
|
||||
}
|
||||
|
||||
func TestTree(t *testing.T) {
|
||||
max_size := 10
|
||||
tree := NewTree(nil)
|
||||
for i := 0; i < max_size; i++ {
|
||||
tree.Insert(ElementType(i))
|
||||
}
|
||||
assert.Equal(t, 10, tree.Size)
|
||||
assert.Equal(t, ElementType(5), tree.Search(5).Value)
|
||||
assert.Equal(t, true, tree.Delete(5))
|
||||
assert.Equal(t, 9, tree.Size)
|
||||
|
||||
}
|
29
algorithms/graphs/README.md
Normal file
29
algorithms/graphs/README.md
Normal file
@ -0,0 +1,29 @@
|
||||
## 图算法
|
||||
|
||||
### 搜索
|
||||
|
||||
#### 深度优先DFS(Depth First Search)
|
||||
|
||||
如: 树的先序遍历
|
||||
|
||||
思想:假设所有顶点均未被访问,从一个顶点V出发,依次访问它的各个邻接点,直到图中所有和V相通的点都被访问到,若还有未访问的顶点,重复以上过程
|
||||
|
||||
栈
|
||||
|
||||
#### 广度优先BFS(Breadth First Search)
|
||||
|
||||
如:树的层次遍历
|
||||
|
||||
思想: 从顶点V出发,访问V之后依次访问v的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点
|
||||
|
||||
队列
|
||||
|
||||
### 最短路径
|
||||
|
||||
#### 迪杰斯特拉(Dijkstra)
|
||||
|
||||
[详解01](http://data.biancheng.net/view/46.html)
|
||||
|
||||
[详解02](http://www.cnblogs.com/skywang12345/p/3711512.html)
|
||||
|
||||
用于有向网中计算一个节点到其他节点的最短路径
|
30
algorithms/graphs/bfs-shortest-path/bfs_shortest_path.go
Normal file
30
algorithms/graphs/bfs-shortest-path/bfs_shortest_path.go
Normal file
@ -0,0 +1,30 @@
|
||||
package bfs_shortest_path
|
||||
|
||||
import (
|
||||
"algorithms-go/DataStructure/graph"
|
||||
"algorithms-go/algorithms/graphs/bfs"
|
||||
)
|
||||
|
||||
func ShortestPath(g *graph.DirGraph, start graph.VertexId) (dist map[graph.VertexId]int) {
|
||||
dist = make(map[graph.VertexId]int)
|
||||
visited := make(map[graph.VertexId]bool)
|
||||
|
||||
getDist := func(v graph.VertexId) { //目标顶点
|
||||
neighbours := g.GetNeighbours(v).VerticesIter()
|
||||
visited[v] = true
|
||||
|
||||
for neighbour := range neighbours {
|
||||
|
||||
ok, _ := visited[neighbour]
|
||||
if !ok {
|
||||
dist[neighbour] = dist[v] + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
bfs.Bfs(g, start, getDist)
|
||||
return
|
||||
}
|
||||
|
||||
func GetDist(g *graph.DirGraph, from, to graph.VertexId) int {
|
||||
return ShortestPath(g, from)[to]
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package bfs_shortest_path
|
||||
|
||||
import (
|
||||
"algorithms-go/DataStructure/graph"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestShortestPath(t *testing.T) {
|
||||
h := graph.NewDirected()
|
||||
for i := 0; i < 10; i++ {
|
||||
h.AddVertex(graph.VertexId(i))
|
||||
}
|
||||
|
||||
for i := 0; i < 9; i++ {
|
||||
h.AddEdge(graph.VertexId(i), graph.VertexId(i+1), 1)
|
||||
}
|
||||
|
||||
assert.Equal(t, 9, GetDist(h, graph.VertexId(0), graph.VertexId(9)))
|
||||
}
|
27
algorithms/graphs/bfs/bfs.go
Normal file
27
algorithms/graphs/bfs/bfs.go
Normal file
@ -0,0 +1,27 @@
|
||||
package bfs
|
||||
|
||||
import "algorithms-go/DataStructure/graph"
|
||||
|
||||
//有向图的广度优先搜索
|
||||
func Bfs(g *graph.DirGraph, start graph.VertexId, fn func(graph.VertexId)) {
|
||||
queue := []graph.VertexId{start}
|
||||
visited := make(map[graph.VertexId]bool)
|
||||
|
||||
var next []graph.VertexId
|
||||
|
||||
for len(queue) > 0 {
|
||||
next = []graph.VertexId{}
|
||||
for _, vertex := range queue {
|
||||
visited[vertex] = true
|
||||
//获取邻接点
|
||||
neighbours := g.GetNeighbours(vertex).VerticesIter()
|
||||
fn(vertex)
|
||||
for neighbour := range neighbours {
|
||||
if _, ok := visited[neighbour]; !ok {
|
||||
next = append(next, neighbour)
|
||||
}
|
||||
}
|
||||
}
|
||||
queue = next
|
||||
}
|
||||
}
|
24
algorithms/graphs/bfs/bfs_test.go
Normal file
24
algorithms/graphs/bfs/bfs_test.go
Normal file
@ -0,0 +1,24 @@
|
||||
package bfs
|
||||
|
||||
import (
|
||||
"algorithms-go/DataStructure/graph"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBfs(t *testing.T) {
|
||||
h := graph.NewDirected()
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
h.AddVertex(graph.VertexId(i))
|
||||
}
|
||||
|
||||
for i := 0; i < 9; i++ {
|
||||
h.AddEdge(graph.VertexId(i), graph.VertexId(i+1), 1)
|
||||
}
|
||||
count := 0
|
||||
Bfs(h, graph.VertexId(3), func(id graph.VertexId) {
|
||||
count += int(id)
|
||||
})
|
||||
assert.Equal(t, 42, count)
|
||||
}
|
48
algorithms/graphs/dfs/dfs.go
Normal file
48
algorithms/graphs/dfs/dfs.go
Normal file
@ -0,0 +1,48 @@
|
||||
package dfs
|
||||
|
||||
import (
|
||||
"algorithms-go/DataStructure/graph"
|
||||
"algorithms-go/DataStructure/stack"
|
||||
)
|
||||
|
||||
//无向图的深度搜索
|
||||
|
||||
func UndirectedDfs(g *graph.UnGraph, v graph.VertexId, fn func(graph.VertexId)) {
|
||||
s := stack.New() //初始化一个栈
|
||||
s.Push(int(v))
|
||||
visited := make(map[graph.VertexId]bool) //记录是否访问过
|
||||
|
||||
for s.Len() > 0 { //只要栈里面有,就一直
|
||||
v, _ := s.Pop()
|
||||
_v := graph.VertexId(v)
|
||||
if _, ok := visited[_v]; !ok {
|
||||
visited[_v] = true
|
||||
fn(_v)
|
||||
//将此结点的邻接点压栈
|
||||
neighbours := g.GetNeighbours(_v).VerticesIter()
|
||||
for neighbour := range neighbours {
|
||||
s.Push(int(neighbour))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//有向图的深度搜索
|
||||
func DirectedDfs(g *graph.DirGraph, v graph.VertexId, fn func(graph.VertexId)) {
|
||||
s := stack.New()
|
||||
s.Push(int(v))
|
||||
visited := make(map[graph.VertexId]bool)
|
||||
|
||||
for s.Len() > 0 {
|
||||
v, _ := s.Pop()
|
||||
_v := graph.VertexId(v)
|
||||
if _, ok := visited[_v]; !ok {
|
||||
visited[_v] = true
|
||||
fn(_v)
|
||||
neighbours := g.GetSuccessors(_v).VerticesIter()
|
||||
for neighbour := range neighbours {
|
||||
s.Push(int(neighbour))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
47
algorithms/graphs/dfs/dfs_test.go
Normal file
47
algorithms/graphs/dfs/dfs_test.go
Normal file
@ -0,0 +1,47 @@
|
||||
package dfs
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/xiaomeng79/go-algorithm/data-structures/graph"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUndirectedDfs(t *testing.T) {
|
||||
//建立一个无向图
|
||||
h := graph.NewUndirected()
|
||||
|
||||
//增加顶点,
|
||||
for i := 0; i < 10; i++ {
|
||||
h.AddVertex(graph.VertexId(i))
|
||||
}
|
||||
//增加边
|
||||
for i := 0; i < 9; i++ {
|
||||
h.AddEdge(graph.VertexId(i), graph.VertexId(i+1), 1)
|
||||
}
|
||||
counter := 0
|
||||
UndirectedDfs(h, graph.VertexId(4), func(id graph.VertexId) {
|
||||
counter += int(id)
|
||||
})
|
||||
assert.Equal(t, 45, counter)
|
||||
}
|
||||
|
||||
func TestDirectedDfs(t *testing.T) {
|
||||
//建立一个有向图
|
||||
h := graph.NewDirected()
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
v := graph.VertexId(i)
|
||||
h.AddVertex(v)
|
||||
}
|
||||
|
||||
for i := 0; i < 9; i++ {
|
||||
h.AddEdge(graph.VertexId(i), graph.VertexId(i+1), 1)
|
||||
}
|
||||
|
||||
counter := 0
|
||||
DirectedDfs(h, graph.VertexId(3), func(v graph.VertexId) {
|
||||
counter += int(v)
|
||||
})
|
||||
|
||||
assert.Equal(t, 42, counter)
|
||||
}
|
44
algorithms/graphs/dijkstra/dijkstra.go
Normal file
44
algorithms/graphs/dijkstra/dijkstra.go
Normal file
@ -0,0 +1,44 @@
|
||||
package dijkstra
|
||||
|
||||
import (
|
||||
"algorithms-go/DataStructure/graph"
|
||||
"algorithms-go/DataStructure/priority_queue"
|
||||
)
|
||||
|
||||
//迪杰斯特拉算法计算的是从网中一个顶点到其它顶点之间的最短路径问题
|
||||
//http://data.biancheng.net/view/46.html
|
||||
|
||||
func ShortestPath(g *graph.UnGraph, source graph.VertexId) map[graph.VertexId]graph.VertexId {
|
||||
visited := make(map[graph.VertexId]bool, g.VerticesCount())
|
||||
dist := make(map[graph.VertexId]int)
|
||||
prev := make(map[graph.VertexId]graph.VertexId)
|
||||
Q := priority_queue.NewMin()
|
||||
vertices := g.VerticesIter()
|
||||
|
||||
dist[source] = 0
|
||||
for vertex := range vertices {
|
||||
if source != vertex {
|
||||
dist[vertex] = 1000
|
||||
prev[vertex] = 0
|
||||
}
|
||||
Q.Insert(*priority_queue.NewItem(vertex, dist[vertex]))
|
||||
}
|
||||
|
||||
for Q.Len() > 0 {
|
||||
u := Q.Extract().Value.(graph.VertexId)
|
||||
visited[u] = true
|
||||
|
||||
for neighbour := range g.GetNeighbours(u).VerticesIter() {
|
||||
if !visited[neighbour] {
|
||||
alt := dist[u] + g.GetEdgeWeight(u, neighbour)
|
||||
|
||||
if alt < dist[neighbour] {
|
||||
dist[neighbour] = alt
|
||||
prev[neighbour] = u
|
||||
Q.ChangePriority(neighbour, alt)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return prev
|
||||
}
|
34
algorithms/graphs/dijkstra/dijkstra_test.go
Normal file
34
algorithms/graphs/dijkstra/dijkstra_test.go
Normal file
@ -0,0 +1,34 @@
|
||||
package dijkstra
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/xiaomeng79/go-algorithm/data-structures/graph"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestShortestPath(t *testing.T) {
|
||||
h := graph.NewUndirected()
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
h.AddVertex(graph.VertexId(i))
|
||||
}
|
||||
|
||||
h.AddEdge(graph.VertexId(0), graph.VertexId(1), 10)
|
||||
h.AddEdge(graph.VertexId(1), graph.VertexId(2), 20)
|
||||
h.AddEdge(graph.VertexId(2), graph.VertexId(3), 40)
|
||||
h.AddEdge(graph.VertexId(0), graph.VertexId(2), 50)
|
||||
h.AddEdge(graph.VertexId(0), graph.VertexId(3), 80)
|
||||
h.AddEdge(graph.VertexId(0), graph.VertexId(4), 10)
|
||||
h.AddEdge(graph.VertexId(4), graph.VertexId(3), 10)
|
||||
|
||||
prev := ShortestPath(h, graph.VertexId(0))
|
||||
fmt.Println(prev)
|
||||
if prev[1] != graph.VertexId(0) ||
|
||||
prev[2] != graph.VertexId(1) ||
|
||||
prev[3] != graph.VertexId(4) ||
|
||||
prev[4] != graph.VertexId(0) {
|
||||
|
||||
fmt.Println(prev)
|
||||
t.Error()
|
||||
}
|
||||
}
|
50
algorithms/graphs/topological/topological.go
Normal file
50
algorithms/graphs/topological/topological.go
Normal file
@ -0,0 +1,50 @@
|
||||
package topological
|
||||
|
||||
import (
|
||||
"algorithms-go/DataStructure/graph"
|
||||
"algorithms-go/DataStructure/stack"
|
||||
)
|
||||
|
||||
//拓扑排序指的是将有向无环图(又称“DAG”图)中的顶点按照图中指定的先后顺序进行排序。
|
||||
|
||||
//拓扑排序实质上就是一种偏序到全序的排序算法
|
||||
|
||||
//应用场景:排课问题,课程有依赖关系
|
||||
|
||||
//对有向无环图进行拓扑排序,只需要遵循两个原则:
|
||||
//在图中选择一个没有前驱的顶点 V;
|
||||
//从图中删除顶点 V 和所有以该顶点为尾的弧。
|
||||
|
||||
func Sort(g *graph.DirGraph) *stack.Stack {
|
||||
var visit func(v graph.VertexId)
|
||||
sorted := stack.New()
|
||||
visited := make(map[graph.VertexId]bool)
|
||||
marked := make(map[graph.VertexId]bool)
|
||||
|
||||
visit = func(v graph.VertexId) {
|
||||
//判断是不是一个无环图
|
||||
if marked[v] {
|
||||
panic("not a DAG")
|
||||
}
|
||||
marked[v] = true
|
||||
//获取出度的顶点
|
||||
successors := g.GetSuccessors(v).VerticesIter()
|
||||
for successor := range successors {
|
||||
if !marked[successor] && !visited[successor] {
|
||||
visit(successor)
|
||||
}
|
||||
}
|
||||
visited[v] = true
|
||||
marked[v] = false
|
||||
sorted.Push(int(v))
|
||||
}
|
||||
|
||||
vertices := g.VerticesIter()
|
||||
for vertex := range vertices {
|
||||
if !visited[vertex] {
|
||||
visit(vertex)
|
||||
}
|
||||
}
|
||||
|
||||
return sorted
|
||||
}
|
41
algorithms/graphs/topological/topological_test.go
Normal file
41
algorithms/graphs/topological/topological_test.go
Normal file
@ -0,0 +1,41 @@
|
||||
package topological
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/xiaomeng79/go-algorithm/data-structures/graph"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSort(t *testing.T) {
|
||||
h := graph.NewDirected()
|
||||
|
||||
h.AddVertex(graph.VertexId(2))
|
||||
h.AddVertex(graph.VertexId(3))
|
||||
h.AddVertex(graph.VertexId(5))
|
||||
h.AddVertex(graph.VertexId(7))
|
||||
h.AddVertex(graph.VertexId(8))
|
||||
h.AddVertex(graph.VertexId(9))
|
||||
h.AddVertex(graph.VertexId(10))
|
||||
h.AddVertex(graph.VertexId(11))
|
||||
|
||||
h.AddEdge(graph.VertexId(7), graph.VertexId(8), 1)
|
||||
h.AddEdge(graph.VertexId(7), graph.VertexId(11), 1)
|
||||
h.AddEdge(graph.VertexId(5), graph.VertexId(11), 1)
|
||||
h.AddEdge(graph.VertexId(3), graph.VertexId(8), 1)
|
||||
h.AddEdge(graph.VertexId(3), graph.VertexId(10), 1)
|
||||
h.AddEdge(graph.VertexId(8), graph.VertexId(9), 1)
|
||||
h.AddEdge(graph.VertexId(11), graph.VertexId(10), 1)
|
||||
h.AddEdge(graph.VertexId(11), graph.VertexId(2), 1)
|
||||
h.AddEdge(graph.VertexId(11), graph.VertexId(9), 1)
|
||||
|
||||
s := Sort(h)
|
||||
i, _ := s.Pop()
|
||||
first := graph.VertexId(i)
|
||||
if first != 7 &&
|
||||
first != 5 &&
|
||||
first != 3 {
|
||||
fmt.Print(first, first != 7)
|
||||
t.Error()
|
||||
}
|
||||
|
||||
}
|
17
algorithms/sort/README.md
Normal file
17
algorithms/sort/README.md
Normal file
@ -0,0 +1,17 @@
|
||||
排序:
|
||||
将多个元素按照一定规则的顺序进行排列。
|
||||
|
||||
如:
|
||||
数值排序
|
||||
字母排序
|
||||
优先级排序
|
||||
|
||||
分析:
|
||||
排序则必有先后大小高低,基本操作是:将元素通过比较来调整位置。
|
||||
|
||||
排序本质是消除逆序。采用“交换相邻元素”的办法来消除逆序,每次正好只消除一个,因此必须执行O(N^2)的交换次数。
|
||||
基于交换元素的排序要想突破这个下界,必须执行一些比较,交换相隔比较远的元素,使得一次交换能消除一个以上的逆序。这样才能降低时间复杂度。
|
||||
|
||||
[可视化](https://www.cs.usfca.edu/~galles/visualization/Algorithms.html)
|
||||
|
||||
![时间复杂度](https://github.com/hustcc/JS-Sorting-Algorithm/raw/master/res/sort.png)
|
56
algorithms/sort/bubble/bubble.go
Normal file
56
algorithms/sort/bubble/bubble.go
Normal file
@ -0,0 +1,56 @@
|
||||
//冒泡排序
|
||||
package bubble
|
||||
|
||||
//原理:重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成
|
||||
|
||||
//算法步骤:
|
||||
//1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
|
||||
//2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
|
||||
//3. 针对所有的元素重复以上的步骤,除了最后一个。
|
||||
//4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
|
||||
|
||||
//时间复杂度:O(n²)
|
||||
|
||||
//推荐
|
||||
func BubbleSort(arr []int) {
|
||||
for itemCount := len(arr) - 1; ; itemCount-- {
|
||||
swap := false //下一次判断是否发生交换,没交换,说明已经有序
|
||||
for i := 1; i <= itemCount; i++ {
|
||||
if arr[i-1] > arr[i] {
|
||||
arr[i-1], arr[i] = arr[i], arr[i-1]
|
||||
swap = true
|
||||
}
|
||||
}
|
||||
if swap == false {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//func BubbleSort(arr []int) []int {
|
||||
// length := len(arr) //获取最大长度
|
||||
// for i := 0; i < length; i++ { //比较次数
|
||||
// for j := 0; j < length-1-i; j++ { //比较长度
|
||||
// if arr[j] > arr[j+1] {
|
||||
// arr[j], arr[j+1] = arr[j+1], arr[j]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return arr
|
||||
//}
|
||||
|
||||
//func BubbleSort2(arr []int) {
|
||||
// l := len(arr)
|
||||
// for i := 0; i<l; i++ {
|
||||
// flag := true
|
||||
// for j:=0;j<l-i-1;j++ {
|
||||
// if arr[j+1] < arr[j] {
|
||||
// arr[j+1],arr[j] = arr[j],arr[j+1]
|
||||
// flag = false
|
||||
// }
|
||||
// }
|
||||
// if flag {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
//}
|
30
algorithms/sort/bubble/bubble_test.go
Normal file
30
algorithms/sort/bubble/bubble_test.go
Normal file
@ -0,0 +1,30 @@
|
||||
package bubble
|
||||
|
||||
import (
|
||||
"algorithms-go/algorithms/sort/testdata"
|
||||
"algorithms-go/algorithms/sort/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBubbleSort(t *testing.T) {
|
||||
for _, v := range testdata.Values {
|
||||
BubbleSort(v.Nosort)
|
||||
assert.Exactly(t, v.Sort, v.Nosort, "no eq")
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkBubbleSort(n int, b *testing.B) {
|
||||
b.StopTimer()
|
||||
list := utils.GetArrayOfSize(n)
|
||||
b.ReportAllocs()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
BubbleSort(list)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBubbleSort100(b *testing.B) { benchmarkBubbleSort(100, b) }
|
||||
func BenchmarkBubbleSort1000(b *testing.B) { benchmarkBubbleSort(1000, b) }
|
||||
func BenchmarkBubbleSort10000(b *testing.B) { benchmarkBubbleSort(10000, b) }
|
||||
func BenchmarkBubbleSort100000(b *testing.B) { benchmarkBubbleSort(100000, b) }
|
59
algorithms/sort/bucket/bucket.go
Normal file
59
algorithms/sort/bucket/bucket.go
Normal file
@ -0,0 +1,59 @@
|
||||
package bucket
|
||||
|
||||
//桶排序
|
||||
//注意:不能进行负数排序
|
||||
/*
|
||||
算法步骤:
|
||||
1.获取最大值
|
||||
2.创建一个最大值长度的数组作为桶数组
|
||||
3.循环待排序数组,将每个数组的值作为桶的键在桶数组上叠加
|
||||
4.输出数组
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
基数排序有两种方法:
|
||||
|
||||
这三种排序算法都利用了桶的概念,但对桶的使用方法上有明显差异:
|
||||
|
||||
基数排序:根据键值的每位数字来分配桶;
|
||||
计数排序:每个桶只存储单一键值;
|
||||
桶排序:每个桶存储一定范围的数值;
|
||||
*/
|
||||
|
||||
func BucketSort(arr []int) []int {
|
||||
length := len(arr)
|
||||
if length < 2 {
|
||||
return arr
|
||||
}
|
||||
//获取最大值
|
||||
max := getMaxVal(arr)
|
||||
//新建桶数组
|
||||
bucket := make([]int, max+1)
|
||||
sortedIndex := 0
|
||||
//以待排序数组的值为桶的键,进行统计
|
||||
for i := 0; i < length; i++ {
|
||||
bucket[arr[i]] += 1
|
||||
}
|
||||
//将桶的数据输出
|
||||
for j := 0; j <= max; j++ {
|
||||
for bucket[j] > 0 {
|
||||
arr[sortedIndex] = j
|
||||
sortedIndex += 1
|
||||
bucket[j] -= 1
|
||||
}
|
||||
}
|
||||
|
||||
return arr
|
||||
|
||||
}
|
||||
|
||||
func getMaxVal(arr []int) int {
|
||||
max := arr[0]
|
||||
for i := 0; i < len(arr); i++ {
|
||||
if arr[i] > max {
|
||||
max = arr[i]
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
34
algorithms/sort/bucket/bucket_test.go
Normal file
34
algorithms/sort/bucket/bucket_test.go
Normal file
@ -0,0 +1,34 @@
|
||||
package bucket
|
||||
|
||||
import (
|
||||
"algorithms-go/algorithms/sort/testdata"
|
||||
"algorithms-go/algorithms/sort/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBucketSort(t *testing.T) {
|
||||
i := 0
|
||||
for _, v := range testdata.Values {
|
||||
i++
|
||||
if i == 2 {
|
||||
break
|
||||
}
|
||||
assert.Exactly(t, v.Sort, BucketSort(v.Nosort), "no eq")
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkBucketSort(n int, b *testing.B) {
|
||||
b.StopTimer()
|
||||
list := utils.GetArrayOfSize(n)
|
||||
b.ReportAllocs()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
BucketSort(list)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBucketSort100(b *testing.B) { benchmarkBucketSort(100, b) }
|
||||
func BenchmarkBucketSort1000(b *testing.B) { benchmarkBucketSort(1000, b) }
|
||||
func BenchmarkBucketSort10000(b *testing.B) { benchmarkBucketSort(10000, b) }
|
||||
func BenchmarkBucketSort100000(b *testing.B) { benchmarkBucketSort(100000, b) }
|
18
algorithms/sort/heap/heap.go
Normal file
18
algorithms/sort/heap/heap.go
Normal file
@ -0,0 +1,18 @@
|
||||
package heap
|
||||
|
||||
import "algorithms-go/DataStructure/heap"
|
||||
|
||||
//堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法
|
||||
//堆排序的平均时间复杂度为 Ο(nlogn)
|
||||
|
||||
func HeapSort(arr []int) []int {
|
||||
h := heap.NewMin()
|
||||
for i := 0; i < len(arr); i++ {
|
||||
h.Insert(heap.Int(arr[i]))
|
||||
}
|
||||
|
||||
for i := 0; i < len(arr); i++ {
|
||||
arr[i] = int(h.Extract().(heap.Int))
|
||||
}
|
||||
return arr
|
||||
}
|
30
algorithms/sort/heap/heap_test.go
Normal file
30
algorithms/sort/heap/heap_test.go
Normal file
@ -0,0 +1,30 @@
|
||||
package heap
|
||||
|
||||
import (
|
||||
"algorithms-go/algorithms/sort/testdata"
|
||||
"algorithms-go/algorithms/sort/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHeapSort(t *testing.T) {
|
||||
for _, v := range testdata.Values {
|
||||
assert.Exactly(t, v.Sort, HeapSort(v.Nosort), "no eq")
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkHeapSort(n int, b *testing.B) {
|
||||
b.StopTimer()
|
||||
list := utils.GetArrayOfSize(n)
|
||||
b.ReportAllocs()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
HeapSort(list)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHeapSort100(b *testing.B) { benchmarkHeapSort(100, b) }
|
||||
func BenchmarkHeapSort1000(b *testing.B) { benchmarkHeapSort(1000, b) }
|
||||
func BenchmarkHeapSort10000(b *testing.B) { benchmarkHeapSort(10000, b) }
|
||||
|
||||
//func BenchmarkHeapSort100000(b *testing.B) { benchmarkHeapSort(100000, b) }
|
53
algorithms/sort/insertion/insertion.go
Normal file
53
algorithms/sort/insertion/insertion.go
Normal file
@ -0,0 +1,53 @@
|
||||
//插入排序
|
||||
package insertion
|
||||
|
||||
//原理:通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入
|
||||
//稳定性:稳定
|
||||
//算法步骤:
|
||||
// 1.第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列
|
||||
// 2.从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面
|
||||
|
||||
//[]int{3}
|
||||
//[]int{3,44,56,38,77,38,26}
|
||||
|
||||
func InsertionSort(arr []int) []int {
|
||||
for i := range arr {
|
||||
preIndex := i - 1
|
||||
current := arr[i]
|
||||
for preIndex >= 0 && arr[preIndex] > current {
|
||||
arr[preIndex+1] = arr[preIndex]
|
||||
preIndex -= 1
|
||||
}
|
||||
arr[preIndex+1] = current
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
func InsertionSort2(arr []int) []int {
|
||||
length := len(arr)
|
||||
//if length == 1 {return arr}
|
||||
|
||||
for i := 1; i < length; i++ {
|
||||
backup := arr[i]
|
||||
j := i - 1
|
||||
//将选出的被排数比较后插入左边有序区
|
||||
for j >= 0 && backup < arr[j] { //注意j >= 0必须在前边,否则会数组越界
|
||||
arr[j+1] = arr[j] //移动有序数组
|
||||
j-- //反向移动下标
|
||||
}
|
||||
arr[j+1] = backup //插队插入移动后的空位
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
func InsertionSort3(arr []int) {
|
||||
for i := 1; i < len(arr); i++ {
|
||||
value := arr[i]
|
||||
j := i - 1 //比较的下标
|
||||
for j >= 0 && arr[j] > value {
|
||||
arr[j+1] = arr[j]
|
||||
j = j - 1
|
||||
}
|
||||
arr[j+1] = value
|
||||
}
|
||||
}
|
51
algorithms/sort/insertion/insertion_test.go
Normal file
51
algorithms/sort/insertion/insertion_test.go
Normal file
@ -0,0 +1,51 @@
|
||||
package insertion
|
||||
|
||||
import (
|
||||
"algorithms-go/algorithms/sort/testdata"
|
||||
"algorithms-go/algorithms/sort/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInsertionSort(t *testing.T) {
|
||||
for _, v := range testdata.Values {
|
||||
assert.Exactly(t, v.Sort, InsertionSort(v.Nosort), "no eq")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInsertionSort3(t *testing.T) {
|
||||
for _, v := range testdata.Values {
|
||||
InsertionSort3(v.Nosort)
|
||||
assert.Exactly(t, v.Sort, v.Nosort, "no eq")
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkInsertionSort(n int, b *testing.B) {
|
||||
b.StopTimer()
|
||||
list := utils.GetArrayOfSize(n)
|
||||
b.ReportAllocs()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
InsertionSort(list)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkInsertionSort100(b *testing.B) { benchmarkInsertionSort(100, b) }
|
||||
func BenchmarkInsertionSort1000(b *testing.B) { benchmarkInsertionSort(1000, b) }
|
||||
func BenchmarkInsertionSort10000(b *testing.B) { benchmarkInsertionSort(10000, b) }
|
||||
func BenchmarkInsertionSort100000(b *testing.B) { benchmarkInsertionSort(100000, b) }
|
||||
|
||||
func benchmarkInsertionSort3(n int, b *testing.B) {
|
||||
b.StopTimer()
|
||||
list := utils.GetArrayOfSize(n)
|
||||
b.ReportAllocs()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
InsertionSort3(list)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkInsertionSort3_100(b *testing.B) { benchmarkInsertionSort3(100, b) }
|
||||
func BenchmarkInsertionSort3_1000(b *testing.B) { benchmarkInsertionSort3(1000, b) }
|
||||
func BenchmarkInsertionSort3_10000(b *testing.B) { benchmarkInsertionSort3(10000, b) }
|
||||
func BenchmarkInsertionSort3_100000(b *testing.B) { benchmarkInsertionSort3(100000, b) }
|
81
algorithms/sort/merge/merge.go
Normal file
81
algorithms/sort/merge/merge.go
Normal file
@ -0,0 +1,81 @@
|
||||
package merge
|
||||
|
||||
//归并排序:将两个的有序数列合并成一个有序数列
|
||||
|
||||
//算法步骤(从上到下):
|
||||
//1.申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
|
||||
//2.设定两个指针,最初位置分别为两个已经排序序列的起始位置;
|
||||
//3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
|
||||
//4.重复步骤 3 直到某一指针达到序列尾;
|
||||
//5.将另一序列剩下的所有元素直接复制到合并序列尾。
|
||||
|
||||
//第一种方法
|
||||
func MergeSort(arr []int) []int {
|
||||
length := len(arr)
|
||||
if length < 2 {
|
||||
return arr
|
||||
}
|
||||
mid := length / 2
|
||||
return merge(MergeSort(arr[0:mid]), MergeSort(arr[mid:]))
|
||||
}
|
||||
|
||||
func merge(left, right []int) []int {
|
||||
var result []int
|
||||
for len(left) != 0 && len(right) != 0 {
|
||||
if left[0] <= right[0] {
|
||||
result = append(result, left[0])
|
||||
left = left[1:]
|
||||
} else {
|
||||
result = append(result, right[0])
|
||||
right = right[1:]
|
||||
}
|
||||
}
|
||||
|
||||
if len(left) != 0 {
|
||||
result = append(result, left...)
|
||||
}
|
||||
if len(right) != 0 {
|
||||
result = append(result, right...)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
//第二种方法:推荐
|
||||
func sort(arr []int) {
|
||||
var s = make([]int, len(arr)/2+1)
|
||||
if len(arr) < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
mid := len(arr) / 2
|
||||
|
||||
sort(arr[:mid])
|
||||
sort(arr[mid:])
|
||||
|
||||
if arr[mid-1] <= arr[mid] {
|
||||
return
|
||||
}
|
||||
|
||||
copy(s, arr[:mid])
|
||||
|
||||
l, r := 0, mid
|
||||
|
||||
for i := 0; ; i++ {
|
||||
if s[l] <= arr[r] {
|
||||
arr[i] = s[l]
|
||||
l++
|
||||
|
||||
if l == mid {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
arr[i] = arr[r]
|
||||
r++
|
||||
if r == len(arr) {
|
||||
copy(arr[i+1:], s[l:mid])
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
45
algorithms/sort/merge/merge_test.go
Normal file
45
algorithms/sort/merge/merge_test.go
Normal file
@ -0,0 +1,45 @@
|
||||
package merge
|
||||
|
||||
import (
|
||||
"algorithms-go/algorithms/sort/testdata"
|
||||
"algorithms-go/algorithms/sort/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMergeSort(t *testing.T) {
|
||||
for _, v := range testdata.Values {
|
||||
assert.Exactly(t, v.Sort, MergeSort(v.Nosort), "no eq")
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkMergeSort(n int, b *testing.B) {
|
||||
b.StopTimer()
|
||||
list := utils.GetArrayOfSize(n)
|
||||
b.ReportAllocs()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
//sort(list)
|
||||
MergeSort(list)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMergeSort100(b *testing.B) { benchmarkMergeSort(100, b) }
|
||||
func BenchmarkMergeSort1000(b *testing.B) { benchmarkMergeSort(1000, b) }
|
||||
func BenchmarkMergeSort10000(b *testing.B) { benchmarkMergeSort(10000, b) }
|
||||
func BenchmarkMergeSort100000(b *testing.B) { benchmarkMergeSort(100000, b) }
|
||||
|
||||
func benchmarkMergeSort2(n int, b *testing.B) {
|
||||
b.StopTimer()
|
||||
list := utils.GetArrayOfSize(n)
|
||||
b.ReportAllocs()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sort(list)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMergeSort2_100(b *testing.B) { benchmarkMergeSort2(100, b) }
|
||||
func BenchmarkMergeSort2_1000(b *testing.B) { benchmarkMergeSort2(1000, b) }
|
||||
func BenchmarkMergeSort2_10000(b *testing.B) { benchmarkMergeSort2(10000, b) }
|
||||
func BenchmarkMergeSort2_100000(b *testing.B) { benchmarkMergeSort2(100000, b) }
|
BIN
algorithms/sort/quick/prof.out
Normal file
BIN
algorithms/sort/quick/prof.out
Normal file
Binary file not shown.
45
algorithms/sort/quick/quick.go
Normal file
45
algorithms/sort/quick/quick.go
Normal file
@ -0,0 +1,45 @@
|
||||
package quick
|
||||
|
||||
//快速排序:快速排序应该算是在冒泡排序基础上的递归分治法
|
||||
//主要思想:之所以快,跳跃式的交换消除逆序,
|
||||
|
||||
//算法步骤:
|
||||
//从数列中挑出一个元素,称为 “基准”(pivot);
|
||||
//
|
||||
//重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
|
||||
//
|
||||
//递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
|
||||
//
|
||||
//递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
|
||||
|
||||
func QuickSort(arr []int) []int {
|
||||
return quickSort(arr, 0, len(arr)-1)
|
||||
}
|
||||
|
||||
func quickSort(arr []int, left, right int) []int {
|
||||
if left < right {
|
||||
partitionIndex := partition(arr, left, right) //找分区点
|
||||
quickSort(arr, left, partitionIndex-1)
|
||||
quickSort(arr, partitionIndex+1, right)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
func partition(arr []int, left, right int) int {
|
||||
pivot := left
|
||||
index := pivot + 1
|
||||
|
||||
for i := index; i <= right; i++ {
|
||||
if arr[i] < arr[pivot] { //这里是关键,找到一个比基准大的数和一个比基准小的数进行交换
|
||||
swap(arr, i, index)
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
swap(arr, pivot, index-1) //将基准交换到中间位置
|
||||
return index - 1
|
||||
|
||||
}
|
||||
|
||||
func swap(arr []int, i, j int) {
|
||||
arr[i], arr[j] = arr[j], arr[i]
|
||||
}
|
BIN
algorithms/sort/quick/quick.test
Normal file
BIN
algorithms/sort/quick/quick.test
Normal file
Binary file not shown.
29
algorithms/sort/quick/quick_test.go
Normal file
29
algorithms/sort/quick/quick_test.go
Normal file
@ -0,0 +1,29 @@
|
||||
package quick
|
||||
|
||||
import (
|
||||
"algorithms-go/algorithms/sort/testdata"
|
||||
"algorithms-go/algorithms/sort/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestQuickSort(t *testing.T) {
|
||||
for _, v := range testdata.Values {
|
||||
assert.Exactly(t, v.Sort, QuickSort(v.Nosort), "no eq")
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkQuickSort(n int, b *testing.B) {
|
||||
b.StopTimer()
|
||||
list := utils.GetArrayOfSize(n)
|
||||
b.ReportAllocs()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
QuickSort(list)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkQuickSort100(b *testing.B) { benchmarkQuickSort(100, b) }
|
||||
func BenchmarkQuickSort1000(b *testing.B) { benchmarkQuickSort(1000, b) }
|
||||
func BenchmarkQuickSort10000(b *testing.B) { benchmarkQuickSort(10000, b) }
|
||||
func BenchmarkQuickSort100000(b *testing.B) { benchmarkQuickSort(100000, b) }
|
2358
algorithms/sort/quick/torch.svg
Normal file
2358
algorithms/sort/quick/torch.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 201 KiB |
25
algorithms/sort/selection/selection.go
Normal file
25
algorithms/sort/selection/selection.go
Normal file
@ -0,0 +1,25 @@
|
||||
//选择排序
|
||||
package selection
|
||||
|
||||
//原理:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置
|
||||
//稳定: 不稳定
|
||||
//适用: 数据规模越小越好
|
||||
//算法步骤:
|
||||
//1. 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
|
||||
//2. 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾
|
||||
//3. 重复第二步,直到所有元素均排序完毕
|
||||
|
||||
func SelectionSort(arr []int) []int {
|
||||
length := len(arr)
|
||||
for i := 0; i < length-1; i++ {
|
||||
minIndex := i
|
||||
for j := i + 1; j < length; j++ {
|
||||
if arr[j] < arr[minIndex] {
|
||||
minIndex = j
|
||||
}
|
||||
}
|
||||
//
|
||||
arr[minIndex], arr[i] = arr[i], arr[minIndex]
|
||||
}
|
||||
return arr
|
||||
}
|
29
algorithms/sort/selection/selection_test.go
Normal file
29
algorithms/sort/selection/selection_test.go
Normal file
@ -0,0 +1,29 @@
|
||||
package selection
|
||||
|
||||
import (
|
||||
"algorithms-go/algorithms/sort/testdata"
|
||||
"algorithms-go/algorithms/sort/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSelectionSort(t *testing.T) {
|
||||
for _, v := range testdata.Values {
|
||||
assert.Exactly(t, v.Sort, SelectionSort(v.Nosort), "no eq")
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkSelectionSort(n int, b *testing.B) {
|
||||
b.StopTimer()
|
||||
list := utils.GetArrayOfSize(n)
|
||||
b.ReportAllocs()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
SelectionSort(list)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSelectionSort100(b *testing.B) { benchmarkSelectionSort(100, b) }
|
||||
func BenchmarkSelectionSort1000(b *testing.B) { benchmarkSelectionSort(1000, b) }
|
||||
func BenchmarkSelectionSort10000(b *testing.B) { benchmarkSelectionSort(10000, b) }
|
||||
func BenchmarkSelectionSort100000(b *testing.B) { benchmarkSelectionSort(100000, b) }
|
39
algorithms/sort/shell/shell.go
Normal file
39
algorithms/sort/shell/shell.go
Normal file
@ -0,0 +1,39 @@
|
||||
//希尔排序
|
||||
package shell
|
||||
|
||||
//原理:也称递减增量排序算法,先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
|
||||
//稳定性:不稳定
|
||||
//比较: 1万以内用插入排序,以上使用希尔排序
|
||||
//算法步骤:
|
||||
//1. 选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
|
||||
//2. 按增量序列个数 k,对序列进行 k 趟排序;
|
||||
//3. 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
|
||||
|
||||
func ShellSort(arr []int) []int {
|
||||
length := len(arr)
|
||||
//设置增量
|
||||
gap := length / 2
|
||||
//结束条件,增量为<=0
|
||||
for gap > 0 {
|
||||
//根据增量,进行分组插入排序
|
||||
for i := 0; i < gap; i++ {
|
||||
insertsort(arr, i, gap)
|
||||
}
|
||||
//分组完,组排序后,重新设置增量
|
||||
gap /= 2
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
func insertsort(arr []int, start, gap int) {
|
||||
length := len(arr)
|
||||
for i := start + gap; i < length; i += gap {
|
||||
preindex := i - gap
|
||||
cur := arr[i]
|
||||
for preindex >= 0 && cur < arr[preindex] {
|
||||
arr[preindex+gap] = arr[preindex]
|
||||
preindex -= gap
|
||||
}
|
||||
arr[preindex+gap] = cur
|
||||
}
|
||||
}
|
29
algorithms/sort/shell/shell_test.go
Normal file
29
algorithms/sort/shell/shell_test.go
Normal file
@ -0,0 +1,29 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"algorithms-go/algorithms/sort/testdata"
|
||||
"algorithms-go/algorithms/sort/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestShell(t *testing.T) {
|
||||
for _, v := range testdata.Values {
|
||||
assert.Exactly(t, v.Sort, ShellSort(v.Nosort), "no eq")
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkShellSort(n int, b *testing.B) {
|
||||
b.StopTimer()
|
||||
list := utils.GetArrayOfSize(n)
|
||||
b.ReportAllocs()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ShellSort(list)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkShellSort100(b *testing.B) { benchmarkShellSort(100, b) }
|
||||
func BenchmarkShellSort1000(b *testing.B) { benchmarkShellSort(1000, b) }
|
||||
func BenchmarkShellSort10000(b *testing.B) { benchmarkShellSort(10000, b) }
|
||||
func BenchmarkShellSort100000(b *testing.B) { benchmarkShellSort(100000, b) }
|
12
algorithms/sort/testdata/sortdata.go
vendored
Normal file
12
algorithms/sort/testdata/sortdata.go
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
package testdata
|
||||
|
||||
var (
|
||||
Values = []struct {
|
||||
Nosort []int
|
||||
Sort []int
|
||||
}{
|
||||
{[]int{3, 44, 56, 38, 77, 38, 26}, []int{3, 26, 38, 38, 44, 56, 77}},
|
||||
{[]int{3}, []int{3}},
|
||||
{[]int{3, -1, -6, 34, -78}, []int{-78, -6, -1, 3, 34}},
|
||||
}
|
||||
)
|
1
algorithms/sort/utils/README.md
Normal file
1
algorithms/sort/utils/README.md
Normal file
@ -0,0 +1 @@
|
||||
#### 生成随机数
|
62
algorithms/sort/utils/helper.go
Normal file
62
algorithms/sort/utils/helper.go
Normal file
@ -0,0 +1,62 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
File_Name = "../utils/intarray.txt"
|
||||
Num_Vol = 100000
|
||||
)
|
||||
|
||||
//获取文件绝对路径
|
||||
func absPath() string {
|
||||
_fpath, err := filepath.Abs(File_Name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return _fpath
|
||||
}
|
||||
|
||||
//判断随机数文件是否存在
|
||||
func pathExists() bool {
|
||||
_fpath := absPath()
|
||||
_, err := os.Stat(_fpath)
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//生成随机数文件
|
||||
func gen() {
|
||||
_fpath := absPath()
|
||||
GenRandFile(Num_Vol, _fpath)
|
||||
}
|
||||
|
||||
//获取随机数
|
||||
func GetArrayOfSize(n int) []int {
|
||||
_fpath := absPath()
|
||||
//判断文件是否存在
|
||||
if !pathExists() {
|
||||
gen() //不存在创建
|
||||
}
|
||||
//存在读取
|
||||
f, err := os.Open(_fpath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer f.Close()
|
||||
numbers := make([]int, n)
|
||||
scanner := bufio.NewScanner(f)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
scanner.Scan()
|
||||
s, _ := strconv.Atoi(scanner.Text())
|
||||
numbers = append(numbers, s)
|
||||
}
|
||||
return numbers
|
||||
}
|
100000
algorithms/sort/utils/intarray.txt
Normal file
100000
algorithms/sort/utils/intarray.txt
Normal file
File diff suppressed because it is too large
Load Diff
26
algorithms/sort/utils/randIntGen.go
Normal file
26
algorithms/sort/utils/randIntGen.go
Normal file
@ -0,0 +1,26 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
//生成随机数
|
||||
func getRandInt(anchor int) int {
|
||||
rand.Seed(int64(anchor))
|
||||
return rand.Intn(100000)
|
||||
}
|
||||
|
||||
//生成随机数文件
|
||||
func GenRandFile(num int, filepath string) {
|
||||
f, err := os.Create(filepath)
|
||||
defer f.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for i := 0; i < num; i++ {
|
||||
io.WriteString(f, strconv.Itoa(getRandInt(i))+"\n")
|
||||
}
|
||||
}
|
29
base/排序/归并排序/main.go
Normal file
29
base/排序/归并排序/main.go
Normal file
@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
var (
|
||||
a [100000]int
|
||||
tmp [100000]int
|
||||
)
|
||||
|
||||
func merge_sort(l, r int) {
|
||||
|
||||
}
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 10; i++ {
|
||||
a[i] = rand.Intn(100)
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
fmt.Printf("%d ", a[i])
|
||||
}
|
||||
fmt.Println()
|
||||
merge_sort(0, 9)
|
||||
for i := 0; i < 10; i++ {
|
||||
fmt.Printf("%d ", a[i])
|
||||
}
|
||||
}
|
53
base/排序/快排/main.go
Normal file
53
base/排序/快排/main.go
Normal file
@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
const N int = 1e6 + 10
|
||||
|
||||
var (
|
||||
q [N]int64
|
||||
n int
|
||||
)
|
||||
|
||||
func quickSort(l int, r int) {
|
||||
if l >= r {
|
||||
return
|
||||
}
|
||||
x := q[(l+r) >> 1]
|
||||
i := l - 1
|
||||
j := r + 1
|
||||
for i < j {
|
||||
i++
|
||||
for q[i] < x {
|
||||
i++
|
||||
}
|
||||
j--
|
||||
for q[j] > x {
|
||||
j--
|
||||
}
|
||||
if i < j {
|
||||
temp := q[i]
|
||||
q[i] = q[j]
|
||||
q[j] = temp
|
||||
}
|
||||
}
|
||||
quickSort(l, j)
|
||||
quickSort(j+1, r)
|
||||
}
|
||||
|
||||
func main() {
|
||||
for i:=0;i<10;i++ {
|
||||
q[i] = int64(rand.Intn(100))
|
||||
}
|
||||
for i := 0;i<10;i++ {
|
||||
fmt.Printf("%d ",q[i])
|
||||
}
|
||||
fmt.Println()
|
||||
quickSort(0, 9)
|
||||
for i := 0;i<10;i++ {
|
||||
fmt.Printf("%d ",q[i])
|
||||
}
|
||||
}
|
1
go.mod
1
go.mod
@ -2,3 +2,4 @@ module algorithms-go
|
||||
|
||||
go 1.15
|
||||
|
||||
require github.com/stretchr/testify v1.7.0
|
||||
|
36
go.sum
36
go.sum
@ -1,26 +1,10 @@
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
40
leetcode/初级算法/链表/反转链表/main.go
Normal file
40
leetcode/初级算法/链表/反转链表/main.go
Normal file
@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/**
|
||||
反转一个单链表。
|
||||
|
||||
示例:
|
||||
|
||||
输入: 1->2->3->4->5->NULL
|
||||
输出: 5->4->3->2->1->NULL
|
||||
进阶:
|
||||
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
|
||||
*/
|
||||
|
||||
type ListNode struct {
|
||||
Val int
|
||||
Next *ListNode
|
||||
}
|
||||
|
||||
func reverseList(head *ListNode) *ListNode {
|
||||
if head == nil || head.Next == nil {
|
||||
return head
|
||||
}
|
||||
newHead := reverseList(head.Next)
|
||||
head.Next.Next = head
|
||||
head.Next = nil
|
||||
return newHead
|
||||
}
|
||||
func main() {
|
||||
l := &ListNode{2, &ListNode{1, &ListNode{3, nil}}}
|
||||
a := reverseList(l)
|
||||
|
||||
for a.Next != nil {
|
||||
fmt.Printf("%d",a.Val)
|
||||
}
|
||||
|
||||
}
|
49
leetcode/初级算法/链表/合并两个有序链表/main.go
Normal file
49
leetcode/初级算法/链表/合并两个有序链表/main.go
Normal file
@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/**
|
||||
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
|
||||
输入:l1 = [1,2,4], l2 = [1,3,4]
|
||||
输出:[1,1,2,3,4,4]
|
||||
示例 2:
|
||||
|
||||
输入:l1 = [], l2 = []
|
||||
输出:[]
|
||||
示例 3:
|
||||
|
||||
输入:l1 = [], l2 = [0]
|
||||
输出:[0]
|
||||
提示:
|
||||
|
||||
两个链表的节点数目范围是 [0, 50]
|
||||
-100 <= Node.Val <= 100
|
||||
l1 和 l2 均按 非递减顺序 排列
|
||||
*/
|
||||
type ListNode struct {
|
||||
Val int
|
||||
Next *ListNode
|
||||
}
|
||||
|
||||
func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode {
|
||||
if l1 == nil {
|
||||
return l2
|
||||
}
|
||||
if l2 == nil {
|
||||
return l1
|
||||
}
|
||||
if l1.Val < l2.Val {
|
||||
l1.Next = mergeTwoLists(l1.Next, l2)
|
||||
return l1
|
||||
} else {
|
||||
l2.Next = mergeTwoLists(l1, l2.Next)
|
||||
return l2
|
||||
}
|
||||
}
|
||||
func main() {
|
||||
l := &ListNode{2, &ListNode{1, &ListNode{3, nil}}}
|
||||
a := mergeTwoLists(l,l)
|
||||
fmt.Print(a)
|
||||
}
|
Loading…
Reference in New Issue
Block a user