'添加基础数据结构'

This commit is contained in:
朱毅骏 2021-04-07 18:13:02 +08:00
parent 7e51c05d9a
commit 05d915754d
62 changed files with 105027 additions and 26 deletions

View 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)
}

View 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())
}

View 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. 邻接多重表适用于无向图(网)的存储,该方式避免了使用邻接表存储无向图时出现的存储空间浪费的现象,同时相比邻接表存储无向图,更方便了某些边操作(遍历、删除等)的实现

View 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
}

View 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()
}

View 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)
}
}

View 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
View 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]
}
}

View 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())
}

View 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))
}
}

View 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()
}
}

View 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()
}

View 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]
}

View 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())
}

View 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
}

View 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)
}

View 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
}

View 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. 首先说说为什么要有WALWrite Ahead Log很简单因为数据是先写到内存中如果断电内存中的数据会丢失因此为了保护内存中的数据需要在磁盘上先记录logfile当内存中的数据flush到磁盘上时就可以抛弃相应的Logfile。
2. 什么是memstore, storefile很简单上面说过LSM树就是一堆小树在内存中的小树即memstore每次flush内存中的memstore变成磁盘上一个新的storefile。
3. 为什么会有compact很简单随着小树越来越多读的性能会越来越差数据也有冗余,因此需要在适当的时候对磁盘中的小树进行merge多棵小树变成一颗大树。
#### LSM tree 操作流程如下:
1. 数据写入和更新时首先写入位于内存里的数据结构。为了避免数据丢失也会先写到 WAL 文件中。
2. 内存里的数据结构会定时或者达到固定大小会刷到磁盘。这些磁盘上的文件不会被修改。
3. 随着磁盘上积累的文件越来越多,会定时的进行合并操作,消除冗余数据,减少文件数量。

View 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)
}

View 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)
}

View 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)
用于有向网中计算一个节点到其他节点的最短路径

View 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]
}

View File

@ -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)))
}

View 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
}
}

View 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)
}

View 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))
}
}
}
}

View 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)
}

View 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
}

View 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()
}
}

View 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
}

View 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
View 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)

View 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
// }
// }
//}

View 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) }

View 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
}

View 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) }

View 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
}

View 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) }

View 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
}
}

View 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) }

View 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
}

View 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) }

Binary file not shown.

View 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]
}

Binary file not shown.

View 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) }

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 201 KiB

View 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
}

View 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) }

View File

@ -0,0 +1,39 @@
//希尔排序
package shell
//原理:也称递减增量排序算法,先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
//稳定性:不稳定
//比较: 1万以内用插入排序以上使用希尔排序
//算法步骤:
//1. 选择一个增量序列 t1t2……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
}
}

View 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
View 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}},
}
)

View File

@ -0,0 +1 @@
#### 生成随机数

View 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
}

File diff suppressed because it is too large Load Diff

View 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")
}
}

View 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])
}
}

View 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
View File

@ -2,3 +2,4 @@ module algorithms-go
go 1.15
require github.com/stretchr/testify v1.7.0

36
go.sum
View File

@ -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=

View 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)
}
}

View 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)
}