Skip to content

介绍

一些有趣的编程练习题用来练习学过的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)
}

Released under the MIT License.