diff --git a/codes/go/chapter_stack_and_queue/array_queue.go b/codes/go/chapter_stack_and_queue/array_queue.go new file mode 100644 index 0000000000..f771ea214d --- /dev/null +++ b/codes/go/chapter_stack_and_queue/array_queue.go @@ -0,0 +1,92 @@ +// File: array_queue.go +// Created Time: 2022-11-28 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +import ( + "fmt" + "strings" +) + +// ArrayQueue 基于环形数组实现的队列, 不支持扩容 +type ArrayQueue struct { + data []any // 用于存储队列元素的数组 + capacity int // 队列容量(即最多容量的元素个数) + head int // 头指针,指向队首 + tail int // 尾指针,指向队尾 + 1 +} + +// NewArrayQueue 基于环形数组实现的队列 +func NewArrayQueue(capacity int) *ArrayQueue { + return &ArrayQueue{ + data: make([]any, capacity), + capacity: capacity, + head: 0, + tail: 0, + } +} + +// Size 获取队列的长度 +func (q *ArrayQueue) Size() int { + size := (q.capacity + q.tail - q.head) % q.capacity + return size +} + +// IsEmpty 判断队列是否为空 +func (q *ArrayQueue) IsEmpty() bool { + return q.tail-q.head == 0 +} + +// Offer 入队 +func (q *ArrayQueue) Offer(v any) { + // 当 tail == capacity 表示队列已满 + if q.Size() == q.capacity { + return + } + // 尾结点后添加 + q.data[q.tail] = v + // 尾指针向后移动一位,越过尾部后返回到数组头部 + q.tail = (q.tail + 1) % q.capacity +} + +// Poll 出队 +func (q *ArrayQueue) Poll() any { + if q.IsEmpty() { + return nil + } + v := q.data[q.head] + // 队头指针向后移动,越过尾部后返回到数组头部 + q.head = (q.head + 1) % q.capacity + return v +} + +// Peek 访问队首元素 +func (q *ArrayQueue) Peek() any { + if q.IsEmpty() { + return nil + } + v := q.data[q.head] + return v +} + +func (q *ArrayQueue) Print() { + fmt.Println(q.toString()) +} + +// toString 通过字符串的方式输出 +func (q *ArrayQueue) toString() string { + // 为空时 + if q.IsEmpty() { + return "empty items" + } + var builder strings.Builder + size := q.Size() + str := fmt.Sprintf("%+v", q.data[q.head]) + for i := 1; i < size; i++ { + builder.WriteString(str + " -> ") + str = fmt.Sprintf("%+v", q.data[(i+q.head)%q.capacity]) + } + builder.WriteString(str) + return builder.String() +} diff --git a/codes/go/chapter_stack_and_queue/array_stack.go b/codes/go/chapter_stack_and_queue/array_stack.go new file mode 100644 index 0000000000..f3a8a287d3 --- /dev/null +++ b/codes/go/chapter_stack_and_queue/array_stack.go @@ -0,0 +1,73 @@ +// File: array_stack.go +// Created Time: 2022-11-26 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +import ( + "fmt" + "strconv" + "strings" +) + +// ArrayStack 基于数组实现的栈 +type ArrayStack struct { + data []int // 数据 +} + +func NewArrayStack() *ArrayStack { + return &ArrayStack{ + // 设置栈的长度为 0,容量为 16 + data: make([]int, 0, 16), + } +} + +// Size 栈的长度 +func (s *ArrayStack) Size() int { + return len(s.data) +} + +// IsEmpty 栈是否为空 +func (s *ArrayStack) IsEmpty() bool { + return s.Size() == 0 +} + +// Push 入栈 +func (s *ArrayStack) Push(v int) { + // 切片会自动扩容 + s.data = append(s.data, v) +} + +func (s *ArrayStack) Pop() any { + // 弹出栈前,先判断是否为空 + if s.IsEmpty() { + return nil + } + val := s.Peek() + s.data = s.data[:len(s.data)-1] + return val +} + +func (s *ArrayStack) Peek() any { + if s.IsEmpty() { + return nil + } + val := s.data[len(s.data)-1] + return val +} + +func (s *ArrayStack) Print() { + fmt.Println(s.toString()) +} + +func (s *ArrayStack) toString() string { + var builder strings.Builder + if s.IsEmpty() { + return "empty stack" + } + for i := len(s.data) - 1; i > 0; i-- { + builder.WriteString(strconv.Itoa(s.data[i]) + " -> ") + } + builder.WriteString(strconv.Itoa(s.data[0])) + return builder.String() +} diff --git a/codes/go/chapter_stack_and_queue/deque.go b/codes/go/chapter_stack_and_queue/deque.go new file mode 100644 index 0000000000..3ac070e1a1 --- /dev/null +++ b/codes/go/chapter_stack_and_queue/deque.go @@ -0,0 +1,26 @@ +// File: deque.go +// Created Time: 2022-11-29 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +type Deque interface { + // OfferFirst 元素入队 + OfferFirst(num any) + // OfferLast 元素入队 + OfferLast(num any) + // PeekFirst 访问首元素 + PeekFirst() any + // PeekLast 访问尾元素 + PeekLast() any + // PollFirst 元素出队 + PollFirst() any + // PollLast 元素出队 + PollLast() any + // Size 获取队列长度 + Size() int + // IsEmpty 队列是否为空 + IsEmpty() bool + // toString 队列输出为字符串 + toString() string +} diff --git a/codes/go/chapter_stack_and_queue/deque_test.go b/codes/go/chapter_stack_and_queue/deque_test.go new file mode 100644 index 0000000000..c0eaf089e7 --- /dev/null +++ b/codes/go/chapter_stack_and_queue/deque_test.go @@ -0,0 +1,53 @@ +// File: deque_test.go +// Created Time: 2022-11-29 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +import "testing" + +func TestLinkedListDeque(t *testing.T) { + // 初始化队列 + var deque Deque + deque = NewLinkedListDeque() + + // 元素入队 + deque.OfferLast(2) + deque.OfferLast(5) + deque.OfferLast(4) + deque.OfferFirst(3) + deque.OfferFirst(1) + t.Log("队列 deque = ", deque.toString()) + + // 访问队首元素 + peekFirst := deque.PeekFirst() + t.Log("队首元素 peek = ", peekFirst) + peekLast := deque.PeekLast() + t.Log("队尾元素 peek = ", peekLast) + + // 元素出队 + pollFirst := deque.PollFirst() + t.Log("队首出队元素 pollFirst = ", pollFirst, ",队首出队后 deque = ", deque.toString()) + pollLast := deque.PollLast() + t.Log("队尾出队元素 pollLast = ", pollLast, ",队尾出队后 deque = ", deque.toString()) + + // 获取队的长度 + size := deque.Size() + t.Log("队的长度 size = ", size) + + // 判断是否为空 + isEmpty := deque.IsEmpty() + t.Log("队是否为空 = ", isEmpty) +} + +// BenchmarkArrayQueue 67.92 ns/op in Mac M1 Pro +func BenchmarkLinkedListDeque(b *testing.B) { + stack := NewLinkedListDeque() + // use b.N for looping + for i := 0; i < b.N; i++ { + stack.OfferLast(777) + } + for i := 0; i < b.N; i++ { + stack.PollFirst() + } +} diff --git a/codes/go/chapter_stack_and_queue/linkedlist_deque.go b/codes/go/chapter_stack_and_queue/linkedlist_deque.go new file mode 100644 index 0000000000..62a6149107 --- /dev/null +++ b/codes/go/chapter_stack_and_queue/linkedlist_deque.go @@ -0,0 +1,102 @@ +// File: linkedlist_deque.go +// Created Time: 2022-11-29 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +import ( + "container/list" + "fmt" + "strings" +) + +// LinkedListDeque 基于链表实现的双端队列, 使用内置包 list 来实现栈 +type LinkedListDeque struct { + list *list.List +} + +// NewLinkedListDeque 初始化双端队列 +func NewLinkedListDeque() *LinkedListDeque { + return &LinkedListDeque{ + list: list.New(), + } +} + +// OfferFirst 元素入队 +func (s *LinkedListDeque) OfferFirst(value any) { + s.list.PushFront(value) +} + +// OfferLast 元素入队 +func (s *LinkedListDeque) OfferLast(value any) { + s.list.PushBack(value) +} + +// PollFirst 元素出队 +func (s *LinkedListDeque) PollFirst() any { + if s.IsEmpty() { + return nil + } + e := s.list.Front() + s.list.Remove(e) + return e.Value +} + +// PollLast 元素出队 +func (s *LinkedListDeque) PollLast() any { + if s.IsEmpty() { + return nil + } + e := s.list.Back() + s.list.Remove(e) + return e.Value +} + +// PeekFirst 访问首元素 +func (s *LinkedListDeque) PeekFirst() any { + if s.IsEmpty() { + return nil + } + e := s.list.Front() + return e.Value +} + +// PeekLast 访问尾元素 +func (s *LinkedListDeque) PeekLast() any { + if s.IsEmpty() { + return nil + } + e := s.list.Back() + return e.Value +} + +// Size 获取队列的长度 +func (s *LinkedListDeque) Size() int { + return s.list.Len() +} + +// IsEmpty 判断队列是否为空 +func (s *LinkedListDeque) IsEmpty() bool { + return s.list.Len() == 0 +} + +func (s *LinkedListDeque) Print() { + fmt.Println(s.toString()) +} + +func (s *LinkedListDeque) toString() string { + var builder strings.Builder + if s.IsEmpty() { + fmt.Println("empty stack") + } + e := s.list.Front() + // 强转为 string, 会影响效率 + str := fmt.Sprintf("%v", e.Value) + for e.Next() != nil { + builder.WriteString(str + " -> ") + e = e.Next() + str = fmt.Sprintf("%v", e.Value) + } + builder.WriteString(str) + return builder.String() +} diff --git a/codes/go/chapter_stack_and_queue/linkedlist_queue.go b/codes/go/chapter_stack_and_queue/linkedlist_queue.go new file mode 100644 index 0000000000..5b62ac311f --- /dev/null +++ b/codes/go/chapter_stack_and_queue/linkedlist_queue.go @@ -0,0 +1,78 @@ +// File: linkedlist_queue.go +// Created Time: 2022-11-28 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +import ( + "container/list" + "fmt" + "strings" +) + +// LinkedListQueue 基于链表实现的队列, 使用内置包 list 来实现栈 +type LinkedListQueue struct { + list *list.List +} + +// NewLinkedListQueue 初始化链表 +func NewLinkedListQueue() *LinkedListQueue { + return &LinkedListQueue{ + list: list.New(), + } +} + +// Offer 入队 +func (s *LinkedListQueue) Offer(value any) { + s.list.PushBack(value) +} + +// Poll 出队 +func (s *LinkedListQueue) Poll() any { + if s.IsEmpty() { + return nil + } + e := s.list.Front() + s.list.Remove(e) + return e.Value +} + +// Peek 访问队首元素 +func (s *LinkedListQueue) Peek() any { + if s.IsEmpty() { + return nil + } + e := s.list.Front() + return e.Value +} + +// Size 获取队列的长度 +func (s *LinkedListQueue) Size() int { + return s.list.Len() +} + +// IsEmpty 判断队列是否为空 +func (s *LinkedListQueue) IsEmpty() bool { + return s.list.Len() == 0 +} + +func (s *LinkedListQueue) Print() { + fmt.Println(s.toString()) +} + +func (s *LinkedListQueue) toString() string { + var builder strings.Builder + if s.IsEmpty() { + fmt.Println("empty stack") + } + e := s.list.Front() + // 强转为 string, 会影响效率 + str := fmt.Sprintf("%v", e.Value) + for e.Next() != nil { + builder.WriteString(str + " -> ") + e = e.Next() + str = fmt.Sprintf("%v", e.Value) + } + builder.WriteString(str) + return builder.String() +} diff --git a/codes/go/chapter_stack_and_queue/linkedlist_stack.go b/codes/go/chapter_stack_and_queue/linkedlist_stack.go new file mode 100644 index 0000000000..24f46acb5f --- /dev/null +++ b/codes/go/chapter_stack_and_queue/linkedlist_stack.go @@ -0,0 +1,78 @@ +// File: linkedlist_stack.go +// Created Time: 2022-11-28 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +import ( + "container/list" + "fmt" + "strings" +) + +// LinkedListStack 基于链表实现的栈, 使用内置包 list 来实现栈 +type LinkedListStack struct { + list *list.List +} + +// NewLinkedListStack 初始化链表 +func NewLinkedListStack() *LinkedListStack { + return &LinkedListStack{ + list: list.New(), + } +} + +// Push 入栈 +func (s *LinkedListStack) Push(value int) { + s.list.PushBack(value) +} + +// Pop 出栈 +func (s *LinkedListStack) Pop() any { + if s.IsEmpty() { + return nil + } + e := s.list.Back() + s.list.Remove(e) + return e.Value +} + +// Peek 访问栈顶元素 +func (s *LinkedListStack) Peek() any { + if s.IsEmpty() { + return nil + } + e := s.list.Back() + return e.Value +} + +// Size 获取栈的长度 +func (s *LinkedListStack) Size() int { + return s.list.Len() +} + +// IsEmpty 判断栈是否为空 +func (s *LinkedListStack) IsEmpty() bool { + return s.list.Len() == 0 +} + +func (s *LinkedListStack) Print() { + fmt.Println(s.toString()) +} + +func (s *LinkedListStack) toString() string { + var builder strings.Builder + if s.IsEmpty() { + fmt.Println("empty stack") + } + e := s.list.Back() + // 强转为 string, 会影响效率 + str := fmt.Sprintf("%v", e.Value) + for e.Prev() != nil { + builder.WriteString(str + " -> ") + e = e.Prev() + str = fmt.Sprintf("%v", e.Value) + } + builder.WriteString(str) + return builder.String() +} diff --git a/codes/go/chapter_stack_and_queue/queue.go b/codes/go/chapter_stack_and_queue/queue.go new file mode 100644 index 0000000000..03b97e5a62 --- /dev/null +++ b/codes/go/chapter_stack_and_queue/queue.go @@ -0,0 +1,20 @@ +// File: queue.go +// Created Time: 2022-11-29 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +type Queue interface { + // Offer 元素入队 + Offer(num any) + // Peek 访问首元素 + Peek() any + // Poll 元素出队 + Poll() any + // Size 获取队列长度 + Size() int + // IsEmpty 队列是否为空 + IsEmpty() bool + // toString 队列输出为字符串 + toString() string +} diff --git a/codes/go/chapter_stack_and_queue/queue_test.go b/codes/go/chapter_stack_and_queue/queue_test.go new file mode 100644 index 0000000000..e8c74ecba5 --- /dev/null +++ b/codes/go/chapter_stack_and_queue/queue_test.go @@ -0,0 +1,93 @@ +// File: queue_test.go +// Created Time: 2022-11-28 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +import "testing" + +func TestArrayQueue(t *testing.T) { + // 初始化队列,使用队列的通用接口 + var queue Queue + capacity := 10 + queue = NewArrayQueue(capacity) + + // 元素入队 + queue.Offer(1) + queue.Offer(2) + queue.Offer(3) + queue.Offer(4) + queue.Offer(5) + t.Log("队列 queue = ", queue.toString()) + + // 访问队首元素 + peek := queue.Peek() + t.Log("队首元素 peek = ", peek) + + // 元素出队 + pop := queue.Poll() + t.Log("出队元素 pop = ", pop, ", 出队后 queue =", queue.toString()) + + // 获取队的长度 + size := queue.Size() + t.Log("队的长度 size = ", size) + + // 判断是否为空 + isEmpty := queue.IsEmpty() + t.Log("队是否为空 = ", isEmpty) +} + +func TestLinkedListQueue(t *testing.T) { + // 初始化队 + var queue Queue + queue = NewLinkedListQueue() + + // 元素入队 + queue.Offer(1) + queue.Offer(2) + queue.Offer(3) + queue.Offer(4) + queue.Offer(5) + t.Log("队列 queue = ", queue.toString()) + + // 访问队首元素 + peek := queue.Peek() + t.Log("队首元素 peek = ", peek) + + // 元素出队 + pop := queue.Poll() + t.Log("出队元素 pop = ", pop, ", 出队后 queue =", queue.toString()) + + // 获取队的长度 + size := queue.Size() + t.Log("队的长度 size = ", size) + + // 判断是否为空 + isEmpty := queue.IsEmpty() + t.Log("队是否为空 = ", isEmpty) +} + +// BenchmarkArrayQueue 8 ns/op in Mac M1 Pro +func BenchmarkArrayQueue(b *testing.B) { + capacity := 1000 + stack := NewArrayQueue(capacity) + // use b.N for looping + for i := 0; i < b.N; i++ { + stack.Offer(777) + } + for i := 0; i < b.N; i++ { + stack.Poll() + } +} + +// BenchmarkLinkedQueue 62.66 ns/op in Mac M1 Pro +func BenchmarkLinkedQueue(b *testing.B) { + stack := NewLinkedListQueue() + // use b.N for looping + for i := 0; i < b.N; i++ { + stack.Offer(777) + } + for i := 0; i < b.N; i++ { + stack.Poll() + } +} diff --git a/codes/go/chapter_stack_and_queue/stack.go b/codes/go/chapter_stack_and_queue/stack.go new file mode 100644 index 0000000000..779538784a --- /dev/null +++ b/codes/go/chapter_stack_and_queue/stack.go @@ -0,0 +1,20 @@ +// File: stack.go +// Created Time: 2022-11-26 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +type Stack interface { + // Push 元素入栈 + Push(num int) + // Peek 访问栈顶元素 + Peek() any + // Pop 元素出栈 + Pop() any + // Size 栈的长度 + Size() int + // IsEmpty 栈是否为空 + IsEmpty() bool + // toString 栈输出为字符串 + toString() string +} diff --git a/codes/go/chapter_stack_and_queue/stack_test.go b/codes/go/chapter_stack_and_queue/stack_test.go new file mode 100644 index 0000000000..95c6d2410f --- /dev/null +++ b/codes/go/chapter_stack_and_queue/stack_test.go @@ -0,0 +1,90 @@ +// File: stack_test.go +// Created Time: 2022-11-28 +// Author: Reanon (793584285@qq.com) + +package chapter_stack_and_queue + +import "testing" + +func TestArrayStack(t *testing.T) { + // 初始化栈, 使用接口承接 + var stack Stack + stack = NewArrayStack() + + // 元素入栈 + stack.Push(1) + stack.Push(2) + stack.Push(3) + stack.Push(4) + stack.Push(5) + t.Log("栈 stack = ", stack.toString()) + + // 访问栈顶元素 + peek := stack.Peek() + t.Log("栈顶元素 peek = ", peek) + + // 元素出栈 + pop := stack.Pop() + t.Log("出栈元素 pop = ", pop, ", 出栈后 stack =", stack.toString()) + + // 获取栈的长度 + size := stack.Size() + t.Log("栈的长度 size = ", size) + + // 判断是否为空 + isEmpty := stack.IsEmpty() + t.Log("栈是否为空 = ", isEmpty) +} + +func TestLinkedListStack(t *testing.T) { + // 初始化栈 + var stack Stack + stack = NewLinkedListStack() + // 元素入栈 + stack.Push(1) + stack.Push(2) + stack.Push(3) + stack.Push(4) + stack.Push(5) + t.Log("栈 stack = ", stack.toString()) + + // 访问栈顶元素 + peek := stack.Peek() + t.Log("栈顶元素 peek = ", peek) + + // 元素出栈 + pop := stack.Pop() + t.Log("出栈元素 pop = ", pop, ", 出栈后 stack =", stack.toString()) + + // 获取栈的长度 + size := stack.Size() + t.Log("栈的长度 size = ", size) + + // 判断是否为空 + isEmpty := stack.IsEmpty() + t.Log("栈是否为空 = ", isEmpty) +} + +// BenchmarkArrayStack 8 ns/op in Mac M1 Pro +func BenchmarkArrayStack(b *testing.B) { + stack := NewArrayStack() + // use b.N for looping + for i := 0; i < b.N; i++ { + stack.Push(777) + } + for i := 0; i < b.N; i++ { + stack.Pop() + } +} + +// BenchmarkLinkedListStack 65.02 ns/op in Mac M1 Pro +func BenchmarkLinkedListStack(b *testing.B) { + stack := NewLinkedListStack() + // use b.N for looping + for i := 0; i < b.N; i++ { + stack.Push(777) + } + for i := 0; i < b.N; i++ { + stack.Pop() + } +} diff --git a/codes/go/chapter_tree/binary_search_tree.go b/codes/go/chapter_tree/binary_search_tree.go index 26eb94d73f..ac14e798d7 100644 --- a/codes/go/chapter_tree/binary_search_tree.go +++ b/codes/go/chapter_tree/binary_search_tree.go @@ -5,8 +5,9 @@ package chapter_tree import ( - . "github.com/krahets/hello-algo/pkg" "sort" + + . "github.com/krahets/hello-algo/pkg" ) type BinarySearchTree struct { diff --git a/codes/go/chapter_tree/binary_tree_bfs.go b/codes/go/chapter_tree/binary_tree_bfs.go index d04102f995..5c07a96bc4 100644 --- a/codes/go/chapter_tree/binary_tree_bfs.go +++ b/codes/go/chapter_tree/binary_tree_bfs.go @@ -6,6 +6,7 @@ package chapter_tree import ( "container/list" + . "github.com/krahets/hello-algo/pkg" ) diff --git a/codes/go/chapter_tree/binary_tree_test.go b/codes/go/chapter_tree/binary_tree_test.go index 2c05076dbc..03a85539a4 100644 --- a/codes/go/chapter_tree/binary_tree_test.go +++ b/codes/go/chapter_tree/binary_tree_test.go @@ -5,8 +5,9 @@ package chapter_tree import ( - . "github.com/krahets/hello-algo/pkg" "testing" + + . "github.com/krahets/hello-algo/pkg" ) func TestBinaryTree(t *testing.T) {