介绍
一些有趣的编程练习题用来练习学过的golang知识
数字相关
猜数字游戏
go
package main
import (
"fmt"
"math/rand"
)
func main() {
chances := 3 // 机会次数
isWin := false // 是否已经赢得游戏
fmt.Printf("猜数字游戏开始, 你有 %d 次机会!\n", chances)
minNum := 0
maxNum := 9
fmt.Printf("请输入[%d - %d]范围内的一个数字!\n", minNum, maxNum)
anwserNum := rand.Intn(maxNum) // 谜底数字
// fmt.Println("谜底是:", anwserNum)
for chances > 0 {
chances -= 1
// 获取用户输入
var inputNum int
_, err := fmt.Scanf("%d\n", &inputNum)
if err != nil {
fmt.Printf("❌ 输入错误, 请输入[%d - %d]范围内的一个数字!\n", minNum, maxNum)
break
}
if inputNum < minNum || inputNum > maxNum {
fmt.Printf("输入的数字超出范围, 你还有 %d 次机会!\n", chances)
} else if inputNum < anwserNum {
fmt.Printf("你猜的数字小于答案, 你还有 %d 次机会!\n", chances)
} else if inputNum > anwserNum {
fmt.Printf("你猜的数字大于答案, 你还有 %d 次机会!\n", chances)
} else {
isWin = true
fmt.Println("🎉 恭喜你猜对了, 你赢得了游戏")
break
}
}
if !isWin {
print("😢 游戏结束, 你输了")
}
}向上/向下/四舍五入取整
go
package main
import (
"fmt"
"math"
)
func main() {
f1 := 1.1
f2 := 1.8
f3 := 1.5
f4 := 1.4
// 向上取整
fmt.Printf("%f 向上取整: %f \n", f1, math.Ceil(f1))
fmt.Printf("%f 向上取整: %f \n", f2, math.Ceil(f2))
fmt.Printf("%f 向上取整: %f \n", f3, math.Ceil(f3))
fmt.Printf("%f 向上取整: %f \n", f4, math.Ceil(f4))
// 向下取整
fmt.Printf("%f 向下取整: %f \n", f1, math.Floor(f1))
fmt.Printf("%f 向下取整: %f \n", f2, math.Floor(f2))
fmt.Printf("%f 向下取整: %f \n", f3, math.Floor(f3))
fmt.Printf("%f 向下取整: %f \n", f4, math.Floor(f4))
// 四舍五入取整
fmt.Printf("%f 四舍五入取整: %f \n", f1, math.Round(f1))
fmt.Printf("%f 四舍五入取整: %f \n", f2, math.Round(f2))
fmt.Printf("%f 四舍五入取整: %f \n", f3, math.Round(f3))
fmt.Printf("%f 四舍五入取整: %f \n", f4, math.Round(f4))
}绝对值/较大值/最小值/次方/求余
go
package main
import (
"fmt"
"math"
)
func main() {
// 绝对值
fmt.Printf("%d 的绝对值是 %f \n", -10, math.Abs(-10))
fmt.Printf("%d 的绝对值是 %f \n", 10, math.Abs(10))
// 较大值
fmt.Printf("%d,%d 的较大值是 %f \n", 5, 10, math.Max(5, 10))
fmt.Printf("%d,%d 的较大值是 %f \n", -5, -10, math.Max(-5, -10))
// 较小值
fmt.Printf("%d,%d 的较小值是 %f \n", 5, 10, math.Min(5, 10))
fmt.Printf("%d,%d 的较小值是 %f \n", -5, -10, math.Min(-5, -10))
// 次方
fmt.Printf("%d 的 %d 次方是 %f \n", 1024, 2, math.Pow(1024, 2))
// 求余
fmt.Printf("%d 除以 %d 的余数是 %f \n", 10, 3, math.Mod(10, 3))
}日期时间处理
go
package main
import (
"fmt"
"time"
)
func main() {
// 1.获取当前日期+时间+毫秒(2026-04-01 11:29:46.995084 +0800 CST m=+0.000054350)
now := time.Now()
fmt.Println("当前时间:", now)
// 2.获取当前的日期(2025-09-12)
date := now.Format("2006-01-02")
fmt.Println("当前日期:", date)
// 3.获取格式化的日期+时间字符串(2025-09-12 12:05:22)
datetime := now.Format("2006-01-02 15:04:05")
fmt.Println("格式化的日期时间:", datetime)
// 4.获取当前时间戳
timestamp := now.Unix()
fmt.Println("当前时间戳:", timestamp)
// 5.获取指定时间的时间戳
targetDatetime := "2025-09-12 12:05:22"
targetTimestamp, err := time.Parse("2006-01-02 15:04:05", targetDatetime)
if err != nil {
fmt.Println("解析时间失败:", err)
}
fmt.Println("指定时间戳:", targetTimestamp.Unix())
}字符串处理
格式化
go
package main
import (
"fmt"
"strings"
)
func main() {
// 使用 fmt.Sprintf 会返回一个新字符串
name := "tom"
message := fmt.Sprintf("hello, %s", name)
fmt.Println(message)
// 推荐使用字符串拼接的方式,高性能拼接字符串
var msgBuilder strings.Builder
msgBuilder.Grow(30)
msgBuilder.WriteString("hello ")
msgBuilder.WriteString(name)
msgBuilder.WriteString(",from strings.Builder")
result := msgBuilder.String()
fmt.Println(">", result)
}搜索替换
go
package main
import (
"fmt"
"strings"
)
func main() {
message := "go is best program language in the world!"
//// 搜索 ////
// 1.Index 查询子字符串在字符串中出现的位置, 如果没有找到返回 -1
idx1 := strings.Index(message, "best")
idx2 := strings.Index(message, "bast")
fmt.Println("idx1", idx1) // 6
fmt.Println("idx2", idx2) // -1
// 2.是否以某个字符串开始/结尾
fmt.Println("以 go 开始", strings.HasPrefix(message, "go")) // true
fmt.Println("以 Go 开始", strings.HasPrefix(message, "Go")) // false
fmt.Println("以 world! 结尾", strings.HasSuffix(message, "world!")) // true
fmt.Println("以 World! 结尾", strings.HasSuffix(message, "World!")) // false
// 3.字符串是否包含子字符串
fmt.Println("包含best", strings.Contains(message, "best")) // true
fmt.Println("包含bast", strings.Contains(message, "bast")) // false
//// 替换 ////
// 替换一次
urls := "donwload.apple.com,update.apple.com,shop.apple.com"
results := strings.Replace(urls, "apple", "pear", 1)
fmt.Println("替换一次:", results)
// 替换所有
urls2 := "donwload.apple.com,update.apple.com,shop.apple.com"
results2 := strings.ReplaceAll(urls2, "apple", "pear")
fmt.Println("替换所有:", results2)
}拆分合并
go
package main
import (
"fmt"
"strings"
)
func main() {
message := "go is best program language in the world!"
//// 拆分 ////
items := strings.Split(message, " ")
for index, item := range items {
fmt.Println(index, item)
// 0 go
// 1 is
// 2 best
// 3 program
// 4 language
// 5 in
// 6 the
// 7 world!
}
//// 合并 ////
parts := []string{"go", "is", "a", "good", "program", "language"}
fmt.Println("parts:", parts)
// parts: [go is a good program language]
joined := strings.Join(parts, "-")
fmt.Println("joined string:", joined)
// joined string: go-is-a-good-program-language
}大小写操作
go
package main
import (
"fmt"
"strings"
)
func main() {
// 原字符串
message := "Go is best program language in the world!"
fmt.Println("origin:", message)
// Go is best program language in the world!
// 1.转大写
upperMessage := strings.ToUpper(message)
fmt.Println("upper:", upperMessage)
// GO IS BEST PROGRAM LANGUAGE IN THE WORLD!
// 2.转小写
lowerMessage := strings.ToLower(message)
fmt.Println("lower:", lowerMessage)
// go is best program language in the world!
}填充去除操作
go
package main
import (
"fmt"
"strings"
)
func main() {
// 1.去除指定字符
fmt.Println(strings.Trim("@abc@", "@")) // abc
// 2.去除自定义函数返回的值
trimed := strings.TrimFunc("~abc@", func(r rune) bool {
return r == '~' || r == '@'
})
fmt.Println(trimed) // abc
// 3.去除字符串左边指定的字符
trimedLeft1 := strings.TrimLeft("~abc~", "~")
trimedLeft2 := strings.TrimPrefix("~abc~", "~")
fmt.Println(trimedLeft1) // abc~
fmt.Println(trimedLeft2) // abc~
// 4.去除字符串右边指定的字符
trimedRight1 := strings.TrimRight("~abc~", "~")
trimedRight2 := strings.TrimSuffix("~abc~", "~")
fmt.Println(trimedRight1) // ~abc
fmt.Println(trimedRight2) // ~abc
// 5.去除字符串最左边函数返回的值
trimedLeftFunc := strings.TrimLeftFunc("~@abc@~", func(r rune) bool {
return r == '@' || r == '~'
})
fmt.Println(trimedLeftFunc) // abc@~
// 6.去除字符串最左边函数返回的值
trimedRightFunc := strings.TrimRightFunc("~@abc@~", func(r rune) bool {
return r == '@' || r == '~'
})
fmt.Println(trimedRightFunc) // ~@abc
// 7.去除字符串左右的空格
fmt.Println(strings.TrimSpace(" hello ")) // hello
}转写于编码格式
go
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
// 0.原字符串: 包含 ASCII、中文、emoji
str := "Hello 世界 🌍"
fmt.Println("原始字符串:", str)
// 1.字符串转二进制数据
bytes := []byte(str)
fmt.Printf("1. 字符串转二进制数据(UTF-8 字节): %v\n", bytes)
fmt.Printf("字节数: %d \n", len(bytes))
fmt.Printf("Unicode 码点数: %d \n", utf8.RuneCountInString(str))
// 2.二进制数据转字符串
strFromBytes := string(bytes)
fmt.Printf("2.二进制数据转字符串: %q \n", strFromBytes)
// 3.字符串转 Unicode 码点
runes := []rune(str)
fmt.Printf("3.字符串转 Unicode 码点: %v \n", runes)
// 4.Unicode码点转字符串
strFromRunes := string(runes)
fmt.Printf("4.Unicode码点转字符串: %q \n", strFromRunes)
fmt.Println()
}切片
go
package main
import (
"errors"
"fmt"
"slices"
"strings"
)
func SliceMapFunc[T comparable](originSlice []T, mapFunc func(T, int) T) []T {
result := make([]T, len(originSlice))
for index, value := range originSlice {
result[index] = mapFunc(value, index)
}
return result
}
func SliceFilterFunc[T comparable](originSlice []T, filterFunc func(T, int) bool) []T {
result := make([]T, 0)
for index, value := range originSlice {
if filterFunc(value, index) {
result = append(result, value)
}
}
return result
}
func SliceEveryFunc[T comparable](originSlice []T, handlerFunc func(T, int) bool) bool {
for index, value := range originSlice {
if !handlerFunc(value, index) {
return false
}
}
return true
}
func SliceSomeFunc[T comparable](originSlice []T, handlerFunc func(T, int) bool) bool {
for index, value := range originSlice {
if handlerFunc(value, index) {
return true
}
}
return false
}
func SliceFindFunc[T comparable](originSlice []T, findFunc func(T, int) bool) (T, error) {
var zero T
for index, value := range originSlice {
if findFunc(value, index) {
return value, nil
}
}
return zero, errors.New("cannot find element")
}
func SliceEachFunc[T comparable](originSlice []T, handlerFunc func(T, int) bool) T {
var zero T
for index, value := range originSlice {
handlerFunc(value, index)
}
return zero
}
func SliceShift[T comparable](items []T) []T {
if len(items) == 0 {
return items
}
return items[1:]
}
func SlicePop[T comparable](items []T) []T {
if len(items) == 0 {
return items
}
return items[:len(items)-1]
}
func SliceSplice[T comparable](items []T, start, deleteCount int) []T {
if len(items) == 0 || start < 0 || start >= len(items) || deleteCount <= 0 {
return items
}
if end := start + deleteCount; end > len(items) {
deleteCount = len(items) - start
}
return append(items[:start], items[start+deleteCount:]...)
}
func main() {
// 1.将切片按照指定大小切分为二维切片(返回迭代器)
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
iter := slices.Chunk(nums, 3) // 返回一个迭代器
for chunk := range iter {
fmt.Println("chunk-item:", chunk)
}
// 2.截取切片的一部分 左闭右开区间 nums[startIndex:endIndex]
// 所谓的 "左闭右开区间" 就是包含 startIndex 但 不包含 endIndex
fmt.Println(nums[0:3]) // 截取前3个元素
fmt.Println(nums[1:]) // 从索引 1 开始截取到最后
fmt.Println(nums[:3]) // 从索引 0 开始, 截取到索引 3 之前的元素
// 3.字符串切割为切片
str := "hello golang"
fmt.Println(strings.Split(str, " ")) // 字符串切割为切片
// 4.列表是否包含某个元素
nums = []int{1, 3, 5, 7, 9}
fmt.Println(slices.Contains(nums, 5)) // true
fmt.Println(slices.Contains(nums, 6)) // false
isContains5 := slices.ContainsFunc(nums, func(i int) bool {
return i == 5
})
fmt.Println(isContains5) // true
// 5.拼接两个切片
nums1 := []int{1, 2, 3}
nums2 := []int{4, 5, 6}
nums3 := slices.Concat(nums1, nums2)
fmt.Println(nums3) // [1 2 3 4 5 6]
// 6.遍历切片
nums = []int{1, 3, 5}
for index, value := range nums {
fmt.Println(index, value)
// 0 1
// 1 3
// 2 5
}
// 7.MapFunc 获得新的切片
nums1 = []int{1, 3, 5}
nums2 = SliceMapFunc(nums1, func(i int, index int) int {
return i + 1
})
fmt.Println(nums2)
// 8.过滤符合条件的第一个元素,否则报错
nums1 = []int{1, 3, 5}
first, _ := SliceFindFunc(nums1, func(i int, index int) bool {
return i == 3
})
fmt.Println(first)
// 9.过滤符合条件的所有元素
nums1 = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
nums2 = SliceFilterFunc(nums1, func(i int, index int) bool {
return i%2 == 0
})
fmt.Println(nums2) // 所有偶数
// 10.判断所有元素是否都符合回调函数
biggerThen3 := SliceEveryFunc([]int{5, 6, 7}, func(n int, index int) bool {
return n > 3
})
fmt.Println(biggerThen3) // true
biggerThen5 := SliceEveryFunc([]int{5, 6, 7}, func(n int, index int) bool {
return n > 5
})
fmt.Println(biggerThen5) // false
// 11.判断是否至少有一个元素符合回调函数
biggerThen5 = SliceSomeFunc([]int{5, 6, 7}, func(n int, index int) bool {
return n > 5
})
fmt.Println(biggerThen5) // true
// 12.追加元素
nums = []int{1, 2, 3}
nums = append(nums, 4, 5) // 1.在末尾追加元素
fmt.Println(nums) // [1,2,3,4,5]
nums = slices.Concat([]int{11, 22, 33}, nums) // 2.在开头追加元素
fmt.Println(nums) // [11,22,33,1,2,3,4,5]
nums = append([]int{111, 222}, nums...) // 3.使用 append 在开头追加元素
fmt.Println(nums) // [111,222,11,22,33,1,2,3,4,5]
// 13.删除元素
students := []string{"张三", "李四", "王五", "赵六", "钱七"}
students = SliceShift(students)
fmt.Println("删除第一个元素后:", students)
students = []string{"张三", "李四", "王五", "赵六", "钱七"}
students = SlicePop(students)
fmt.Println("删除最后一个元素后:", students)
students = []string{"张三", "李四", "王五", "赵六", "钱七"}
students = SliceSplice(students, 2, 2)
fmt.Println("删除指定位置的元素后:", students)
// 14.切片排序
// 14.1 默认排序
nums = []int{6, 7, 8, 9, 10, 1, 2, 3, 4, 5}
fmt.Printf("切片是否排序: %v \n", slices.IsSorted(nums))
slices.Sort(nums) // 改变原切片
fmt.Printf("切片排序后: %v \n", nums)
// 14.2 自定义排序 ASC
nums = []int{6, 7, 8, 9, 10, 1, 2, 3, 4, 5}
slices.SortFunc(nums, func(i, j int) int {
if i < j {
return -1
} else if i > j {
return 1
} else {
return 0
}
})
fmt.Printf("切片自定义排序后: %v \n", nums)
// 14.3 自定义排序 DESC
nums = []int{6, 7, 8, 9, 10, 1, 2, 3, 4, 5}
slices.SortFunc(nums, func(i, j int) int {
if i < j {
return 1
} else if i > j {
return -1
} else {
return 0
}
})
fmt.Printf("切片自定义排序后: %v \n", nums)
// 15.翻转数组顺序
nums = []int{6, 7, 8, 9, 10, 1, 2, 3, 4, 5}
slices.Reverse(nums) // 改变原切片
fmt.Printf("切片翻转顺序: %v \n", nums)
}映射
go
package main
import (
"fmt"
"maps"
)
func MapGetWithDefault[T any](m map[string]T, key string, defaultValue T) T {
value, ok := m[key]
if ok {
return value
} else {
return defaultValue
}
}
func main() {
// 0.定义 map
obj := make(map[string]int)
fmt.Println(obj)
obj["a"] = 1
// 1.是否有某个key
value, ok := obj["a"]
if ok {
fmt.Println(value)
} else {
fmt.Println("key 不存在")
}
// 2.获取某个属性的值
// 2.1 获取可能不存在的键, 使用默认值
value = MapGetWithDefault(obj, "b", 2)
fmt.Println(value)
// 3.设置某个属性的值(直接赋值)
obj["b"] = 3
obj["c"] = 4
fmt.Println(obj)
// 4.获取所有 key/value 迭代器
keysIter := maps.Keys(obj)
for key := range keysIter {
fmt.Println(key)
}
valusIter := maps.Values(obj)
for value := range valusIter {
fmt.Println(value)
}
// 5.删除属性
// 5.1 删除某一个属性&值
maps.DeleteFunc(obj, func(k string, v int) bool {
return k == "b"
})
fmt.Println(obj) // map[a:1 c:4]
// 5.2 删除所有属性&值
maps.DeleteFunc(obj, func(k string, v int) bool {
return true
})
fmt.Println(obj) // map[]
}实现 Stack
go
package stack
import "errors"
// 栈结构
type Stack[T comparable] struct {
items []T
}
// 创建一个空的 Stack
func NewStack[T comparable]() *Stack[T] {
return &Stack[T]{
items: make([]T, 0),
}
}
// 栈是否为空
func (s *Stack[T]) IsEmpty() bool {
return len(s.items) == 0
}
// 栈大小(元素个数)
func (s *Stack[T]) Size() int {
return len(s.items)
}
// 入栈
func (s *Stack[T]) Push(item T) {
s.items = append(s.items, item)
}
// 出栈
func (s *Stack[T]) Pop() (T, error) {
var zero T
if s.IsEmpty() {
return zero, errors.New("stack is empty")
}
index := len(s.items) - 1
item := s.items[index]
s.items = s.items[:index]
return item, nil
}
// Peek 查看栈顶元素(不弹出), 如果栈为空返回错误
func (s *Stack[T]) Peek() (T, error) {
var zero T
if s.IsEmpty() {
return zero, errors.New("stack is empty")
}
return s.items[len(s.items)-1], nil
}go
package stack
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestStack(t *testing.T) {
stack := NewStack[int]()
assert.Equal(t, true, stack.IsEmpty())
assert.Equal(t, 0, stack.Size())
stack.Push(1)
assert.Equal(t, false, stack.IsEmpty())
assert.Equal(t, 1, stack.Size())
stack.Push(2)
assert.Equal(t, 2, stack.Size())
stack.Push(3)
assert.Equal(t, 3, stack.Size())
item1, _ := stack.Pop()
assert.Equal(t, 3, item1)
item2, _ := stack.Pop()
assert.Equal(t, 2, item2)
item3, _ := stack.Pop()
assert.Equal(t, 1, item3)
item4, err := stack.Pop()
var zero int
assert.Equal(t, zero, item4) // zero 其实就是 int 类型的"零值" -> 0
assert.EqualError(t, err, "stack is empty")
}实现 Queue
go
package queue
import "errors"
type Queue[T any] struct {
items []T
}
func NewQueue[T any]() *Queue[T] {
return &Queue[T]{
items: make([]T, 0),
}
}
func (q *Queue[T]) IsEmpty() bool {
return len(q.items) == 0
}
func (q *Queue[T]) Size() int {
return len(q.items)
}
func (q *Queue[T]) Enqueue(item T) {
q.items = append(q.items, item)
}
func (q *Queue[T]) Dequeue() (T, error) {
var zero T
if q.IsEmpty() {
return zero, errors.New("queue is empty")
}
index := len(q.items) - 1
element := q.items[index]
q.items = q.items[:index]
return element, nil
}go
package queue
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestQueue(t *testing.T) {
q := NewQueue[string]()
assert.Equal(t, true, q.IsEmpty())
assert.Equal(t, 0, q.Size())
q.Enqueue("zs")
assert.Equal(t, false, q.IsEmpty())
assert.Equal(t, 1, q.Size())
q.Enqueue("ls")
q.Enqueue("wc")
first, _ := q.Dequeue()
assert.Equal(t, "zs", first)
second, _ := q.Dequeue()
assert.Equal(t, "ls", second)
third, _ := q.Dequeue()
assert.Equal(t, "wc", third)
fourth, err := q.Dequeue()
var zero string
assert.Equal(t, zero, fourth)
assert.EqualError(t, err, "queue is empty")
}实现链表
go
package linked_list
// 为了简单, 直接约束泛型为可以比较的类型
type LinkedListNode[T comparable] struct {
element T
next *LinkedListNode[T]
}
func NewLinkedListNode[T comparable](element T) *LinkedListNode[T] {
return &LinkedListNode[T]{
element: element,
next: nil,
}
}
func (node *LinkedListNode[T]) SetNextNode(next *LinkedListNode[T]) {
node.next = next
}
type LinkedList[T comparable] struct {
head *LinkedListNode[T]
tail *LinkedListNode[T]
size int
}
func NewLinkedList[T comparable]() *LinkedList[T] {
return &LinkedList[T]{
head: nil,
tail: nil,
size: 0,
}
}
func FromSlice[T comparable](slice []T) *LinkedList[T] {
ll := NewLinkedList[T]()
for _, element := range slice {
ll.Append(element)
}
return ll
}
func (ll *LinkedList[T]) IsEmpty() bool {
return ll.size == 0
}
func (ll *LinkedList[T]) Size() int {
return ll.size
}
func (ll *LinkedList[T]) Append(element T) {
node := NewLinkedListNode(element)
if ll.IsEmpty() {
ll.head = node
ll.tail = node
} else {
ll.tail.SetNextNode(node)
ll.tail = node
}
ll.size++
}
func (ll *LinkedList[T]) Prepend(element T) {
node := NewLinkedListNode(element)
if ll.IsEmpty() {
ll.head = node
ll.tail = node
} else {
node.SetNextNode(ll.head)
ll.head = node
}
ll.size++
}
func (ll *LinkedList[T]) InsertAt(index int, element T) {
if index < 0 || index > ll.size {
panic("index out of range")
}
if index == 0 {
ll.Prepend(element)
return
}
if index == ll.size {
ll.Append(element)
return
}
// 找到前驱节点 (index-1)
prev := ll.head
for i := 0; i < index-1; i++ {
prev = prev.next
}
// 插入新节点
node := NewLinkedListNode(element)
node.SetNextNode(prev.next)
prev.SetNextNode(node)
ll.size++
}
func (ll *LinkedList[T]) DeleteAt(index int) T {
if index < 0 || index >= ll.size {
panic("index out of range")
}
var element T
if index == 0 {
// 删除头节点
element = ll.head.element
ll.head = ll.head.next
if ll.head == nil {
ll.tail = nil // 链表为空
}
} else {
// 找到前驱节点
prev := ll.head
for i := 0; i < index-1; i++ {
prev = prev.next
}
target := prev.next
element = target.element
// 跳过目标节点
prev.SetNextNode(target.next)
// 如果删除的是尾节点,更新 tail
if index == ll.size-1 {
ll.tail = prev
}
}
ll.size--
return element
}
func (ll *LinkedList[T]) Delete(element T) bool {
if ll.IsEmpty() {
return false
}
// 检查头节点
if ll.head.element == element {
ll.DeleteAt(0)
return true
}
// 遍历查找
prev := ll.head
current := ll.head.next
for current != nil {
if current.element == element {
// 找到了, 删除当前元素
prev.SetNextNode(current.next)
if current == ll.tail {
ll.tail = prev
}
ll.size--
return true
}
prev = current
current = current.next
}
return false
}
func (ll *LinkedList[T]) GetElementAt(index int) T {
if index < 0 || index >= ll.size {
panic("index out of range")
}
current := ll.head
for i := 0; i < index; i++ {
current = current.next
}
return current.element
}
func (ll *LinkedList[T]) Each(handler func(element T)) {
current := ll.head
for current != nil {
handler(current.element)
current = current.next
}
}
func (ll *LinkedList[T]) ToSlice() []T {
result := make([]T, 0, ll.size)
current := ll.head
for current != nil {
result = append(result, current.element)
current = current.next
}
return result
}go
package linked_list
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestLinkedList_NewLinkedList(t *testing.T) {
ll := NewLinkedList[int]()
assert.Equal(t, true, ll.IsEmpty())
assert.Equal(t, 0, ll.Size())
}
func TestLinkedList_FromSlice(t *testing.T) {
ll := FromSlice([]int{1, 2, 3})
assert.Equal(t, false, ll.IsEmpty())
assert.Equal(t, 3, ll.Size())
assert.Equal(t, 1, ll.head.element)
assert.Equal(t, 2, ll.head.next.element)
assert.Equal(t, 3, ll.head.next.next.element)
assert.Equal(t, 3, ll.tail.element)
}
func TestLinkedList_Append(t *testing.T) {
ll := NewLinkedList[int]()
ll.Append(1)
assert.Equal(t, 1, ll.Size())
assert.Equal(t, 1, ll.head.element)
assert.Equal(t, 1, ll.tail.element)
ll.Append(100)
assert.Equal(t, 2, ll.Size())
assert.Equal(t, 1, ll.head.element)
assert.Equal(t, 100, ll.head.next.element)
assert.Equal(t, 100, ll.tail.element)
}
func TestLinkedList_Prepend(t *testing.T) {
ll := NewLinkedList[int]()
ll.Prepend(1)
assert.Equal(t, false, ll.IsEmpty())
assert.Equal(t, 1, ll.Size())
assert.Equal(t, 1, ll.head.element)
assert.Equal(t, 1, ll.tail.element)
ll.Prepend(200)
assert.Equal(t, false, ll.IsEmpty())
assert.Equal(t, 2, ll.Size())
assert.Equal(t, 200, ll.head.element)
assert.Equal(t, 1, ll.head.next.element)
assert.Equal(t, 1, ll.tail.element)
}
func TestLinkedList_InsertAt(t *testing.T) {
ll := NewLinkedList[string]()
ll.Append("a")
ll.Append("b")
ll.Append("c")
// insert before head
ll.InsertAt(0, "before-head")
assert.Equal(t, "before-head", ll.head.element)
assert.Equal(t, "a", ll.head.next.element)
assert.Equal(t, "c", ll.tail.element)
// insert after tail
ll.InsertAt(ll.Size(), "after-tail")
assert.Equal(t, "before-head", ll.head.element)
assert.Equal(t, "a", ll.head.next.element)
assert.Equal(t, "after-tail", ll.tail.element)
// insert middle
ll.InsertAt(1, "middle")
assert.Equal(t, "before-head", ll.head.element)
assert.Equal(t, "middle", ll.head.next.element)
assert.Equal(t, "a", ll.head.next.next.element)
assert.Equal(t, "after-tail", ll.tail.element)
}
func TestLinkedList_DeleteAt(t *testing.T) {
ll := NewLinkedList[string]()
ll.Append("a")
ll.Append("b")
ll.Append("c")
ll.Append("d")
ll.Append("e")
ll.Append("f")
assert.Equal(t, "a", ll.head.element)
assert.Equal(t, "f", ll.tail.element)
// delete head => { b->c->d->e->f }
ll.DeleteAt(0)
assert.Equal(t, 5, ll.Size())
assert.Equal(t, "b", ll.head.element)
assert.Equal(t, "f", ll.tail.element)
// delete tail => { b->c->d->e }
ll.DeleteAt(ll.Size() - 1)
assert.Equal(t, 4, ll.Size())
assert.Equal(t, "b", ll.head.element)
assert.Equal(t, "e", ll.tail.element)
// delete by index => { b->c->e }
ll.DeleteAt(2)
assert.Equal(t, 3, ll.Size())
assert.Equal(t, "b", ll.head.element)
assert.Equal(t, "c", ll.head.next.element)
assert.Equal(t, "e", ll.tail.element)
}
func TestLinkedList_Delete(t *testing.T) {
ll := NewLinkedList[string]()
ll.Append("a")
ll.Append("b")
ll.Append("c")
ll.Append("d")
ll.Delete("b")
assert.Equal(t, false, ll.IsEmpty())
assert.Equal(t, 3, ll.Size())
assert.Equal(t, "a", ll.head.element)
assert.Equal(t, "d", ll.tail.element)
}
func TestLinkedList_GetElementAt(t *testing.T) {
ll := NewLinkedList[string]()
ll.Append("a")
ll.Append("b")
ll.Append("c")
item0 := ll.GetElementAt(0)
assert.Equal(t, "a", item0)
item1 := ll.GetElementAt(1)
assert.Equal(t, "b", item1)
item2 := ll.GetElementAt(2)
assert.Equal(t, "c", item2)
}
func TestLinkedList_Each(t *testing.T) {
ll := NewLinkedList[string]()
ll.Append("a")
ll.Append("b")
ll.Append("c")
var result []string
ll.Each(func(element string) {
result = append(result, element)
})
assert.Equal(t, []string{"a", "b", "c"}, result)
}
func TestLinkedList_ToSlice(t *testing.T) {
ll := NewLinkedList[string]()
ll.Append("a")
ll.Append("b")
ll.Append("c")
assert.Equal(t, false, ll.IsEmpty())
assert.Equal(t, 3, ll.Size())
result := ll.ToSlice()
assert.Equal(t, []string{"a", "b", "c"}, result)
}