1328 字
7 分钟
260715GO算法题常用技巧
GO 算法题常用技巧 & ACM模式模板笔记
一、ACM 模式 I/O 模板
很多 OJ 平台使用 ACM 模式,需要自行处理输入输出
1. fmt.Scan 基础模板
fmt.Scan 会自动跳过空白字符(空格、换行、制表符),逐个读取数据,参数必须传指针(加 &)
package main
import "fmt"
func main() { var n int fmt.Scan(&n)
for i := 0; i < n; i++ { var a, b int fmt.Scan(&a, &b) fmt.Println(a + b) } // 示例输出 // 3 // 7 // 11}2. bufio.Scanner 快读模板(大数据量防超时必备)
数据量大时 fmt.Scan 容易超时,使用 bufio 加速读取,按空白字符拆分输入:
package main
import ( "bufio" "fmt" "os" "strconv")
func main() { scanner := bufio.NewScanner(os.Stdin) // 按空白字符分割(空格、换行都算作分隔符) scanner.Split(bufio.ScanWords) // 扩大缓冲区适配大数据 scanner.Buffer(make([]byte, 1024*1024), 1024*1024)
// 读取 int 的辅助函数 readInt := func() int { scanner.Scan() num, _ := strconv.Atoi(scanner.Text()) return num }
n := readInt() for i := 0; i < n; i++ { a := readInt() b := readInt() fmt.Println(a + b) } // 示例输出 // 3 // 7 // 11}二、排序技巧
1. 基础切片排序
- sort.Ints:int 切片升序快捷排序
- sort.Slice + 闭包:自定义排序规则
package main
import ( "fmt" "sort" "math")
func main() { nums := []int{3, 1, 4, 1, 5, 9}
// 升序(快捷写法) sort.Ints(nums) fmt.Println(nums) // [1 1 3 4 5 9]
// 降序(自定义 less 函数) sort.Slice(nums, func(i, j int) bool { return nums[i] > nums[j] }) fmt.Println(nums) // [9 5 4 3 1 1]
// 按绝对值降序排序 nums1 := []int{1, -3, 5, -9, 4, -1} sort.Slice(nums1, func(i, j int) bool { absI := math.Abs(float64(nums1[i])) absJ := math.Abs(float64(nums1[j])) return absI > absJ }) fmt.Println(nums1) // [-9 5 4 -3 1 -1]
// 按绝对值升序排序 nums2 := []int{1, -3, 5, -9, 4, -1} sort.Slice(nums2, func(i, j int) bool { absI := math.Abs(float64(nums2[i])) absJ := math.Abs(float64(nums2[j])) return absI < absJ }) fmt.Println(nums2) // [1 -1 -3 4 5 -9]}less 函数规则:返回 true 表示 i 元素排在 j 元素前面
2. 结构体切片排序
package main
import ( "fmt" "sort")
type Student struct { Name string Score int}
func main() { students := []Student{ {"Alice", 85}, {"Bob", 92}, {"Charlie", 78}, }
// 按分数降序排列 sort.Slice(students, func(i, j int) bool { return students[i].Score > students[j].Score })
for _, s := range students { fmt.Printf("%s: %d\n", s.Name, s.Score) } // 输出 // Bob: 92 // Alice: 85 // Charlie: 78}3. 多级排序
示例规则:先按分数降序,分数相同则按名字字典序升序
package main
import ( "fmt" "sort")
type Student struct { Name string Score int}
func main() { students := []Student{ {"Charlie", 85}, {"Alice", 92}, {"Bob", 85}, {"Alice", 85}, }
sort.Slice(students, func(i, j int) bool { // 第一关键字:分数降序 if students[i].Score != students[j].Score { return students[i].Score > students[j].Score } // 第二关键字:名字字典序升序 return students[i].Name < students[j].Name })
for _, s := range students { fmt.Printf("%s: %d\n", s.Name, s.Score) } // 输出 // Alice: 92 // Alice: 85 // Bob: 85 // Charlie: 85}三、常用数学操作 & 溢出防范
1. math 包常用函数
⚠️ math 包函数基本只接收 float64 类型,int 需要手动类型转换
package main
import ( "fmt" "math")
func main() { // 绝对值 fmt.Println(math.Abs(-5.0)) x := -5 fmt.Println(int(math.Abs(float64(x)))) // int 类型取绝对值
// 开方 fmt.Println(math.Sqrt(9))
// 幂运算 fmt.Println(math.Pow(2, 3))
// 向下取整、向上取整 fmt.Println(int(math.Floor(3.7))) // 3 fmt.Println(int(math.Ceil(3.2))) // 4}2. 整数边界常量
package main
import ( "fmt" "math")
func main() { // int32 边界 fmt.Println(math.MaxInt32) // 2147483647 fmt.Println(math.MinInt32) // -2147483648
// int64 边界 fmt.Println(math.MaxInt64) // 9223372036854775807
// int32 溢出演示 var n int32 = math.MaxInt32 n = n + 1 fmt.Println(n) // -2147483648,发生溢出
// 防溢出计算:先转为 int64 再运算 var a, b int32 = 100000, 100000 result := int64(a) * int64(b) fmt.Println(result) // 10000000000
// 无穷大初始化技巧 minVal := math.MaxInt32 nums := []int{5, 2, 8, 1, 9} for _, v := range nums { if v < minVal { minVal = v } } fmt.Println(minVal) // 1}- 刷题建议:优先使用原生 int(64位系统等价 int64)降低溢出风险;大数运算显式用 int64
- Go1.21+ 可使用内置 min/max 函数,不要混用 math.Max/math.Min
四、优先队列模板(container/heap)
Go 标准库无现成优先队列,需实现 heap.Interface 接口
1. 小顶堆模板
package main
import ( "container/heap" "fmt")
// IntHeap 实现 heap.Interfacetype IntHeap []int
func (h IntHeap) Len() int { return len(h) }func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] } // 小顶堆核心规则func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *IntHeap) Push(x interface{}) { *h = append(*h, x.(int))}
func (h *IntHeap) Pop() interface{} { old := *h n := len(old) x := old[n-1] *h = old[:n-1] return x}
func main() { h := &IntHeap{} heap.Init(h)
// 入堆 heap.Push(h, 3) heap.Push(h, 1) heap.Push(h, 4) heap.Push(h, 1) heap.Push(h, 5)
// 查看堆顶元素(最小值) fmt.Println((*h)[0]) // 1 // 堆长度 fmt.Println(h.Len()) // 5
// 依次弹出元素 for h.Len() > 0 { fmt.Printf("%d ", heap.Pop(h)) } // 输出:1 1 3 4 5 fmt.Println()}2. 大顶堆修改方法
仅修改 Less 函数比较方向即可:
func (h IntHeap) Less(i, j int) bool { return h[i] > h[j] // 大顶堆核心规则}优先队列使用要点
- 入堆:
heap.Push(h, val),不要直接调用 h.Push - 出堆:
heap.Pop(h),不要直接调用 h.Pop - 查看堆顶:
(*h)[0],直接访问底层切片首位 - 判空:
h.Len() > 0
五、整体刷题小结
- I/O:小规模输入可用 fmt.Scan;大数据量直接用 bufio 快读避免超时
- 排序:优先 sort.Slice + 闭包自定义 less 函数,支持单条件/结构体/多级排序
- 数值计算:注意类型转换和溢出问题,大数运算用 int64
- 优先队列:套用 heap 模板,通过修改 Less 函数实现大小顶堆
- 基础工具:善用 math 包函数,注意 float64 类型限制;可用 MaxInt32 表示无穷大
260715GO算法题常用技巧
https://fuwari.vercel.app/posts/260715go算法题常用技巧/