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.Interface
type 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

五、整体刷题小结#

  1. I/O:小规模输入可用 fmt.Scan;大数据量直接用 bufio 快读避免超时
  2. 排序:优先 sort.Slice + 闭包自定义 less 函数,支持单条件/结构体/多级排序
  3. 数值计算:注意类型转换和溢出问题,大数运算用 int64
  4. 优先队列:套用 heap 模板,通过修改 Less 函数实现大小顶堆
  5. 基础工具:善用 math 包函数,注意 float64 类型限制;可用 MaxInt32 表示无穷大
260715GO算法题常用技巧
https://fuwari.vercel.app/posts/260715go算法题常用技巧/
作者
anh
发布于
2026-07-15
许可协议
CC BY-NC-SA 4.0