5.2 Go 数组进阶学习笔记(多维、排序、搜索)
1. 多维数组 --- 声明与访问
多维数组是"数组的数组",Go 通过 [行][列] 语法定义:
go
package main
import "fmt"
func main() {
// 声明 3x3 矩阵,元素全部为零值 0
var matrix [3][3]int
// 初始化 2x4 数组(2行4列)
grades := [2][4]int{{85, 92, 78, 96}, {88, 91, 84, 87}}
// 修改元素:matrix[行][列]
matrix[1][1] = 5 // 第2行第2列设为5
// 打印矩阵:嵌套 for 循环
fmt.Println("Matrix:")
for i := 0; i < len(matrix); i++ {
for j := 0; j < len(matrix[i]); j++ {
fmt.Printf("%3d ", matrix[i][j]) // %3d 右对齐,宽度3
}
fmt.Println() // 每行末尾换行
}
}
执行结果:
makefile
Matrix:
0 0 0
0 5 0
0 0 0
要点:
[3][3]int--- 3x3 的二维数组,类型是"3个3int的数组"var matrix [3][3]int--- 未初始化,所有元素为零值 0matrix[1][1] = 5--- 先选行(索引1),再选列(索引1),设为5len(matrix)= 3(行数),len(matrix[i])= 3(每行的列数)%3d--- 右对齐,宽度3,保证矩阵格式整齐(0、5)- Go 没有专门的矩阵类型,多维数组就是"数组的数组"
2. 多维数组 --- range 遍历与统计
用嵌套 range 遍历多维数组更简洁:
go
package main
import "fmt"
func main() {
grades := [2][4]int{{85, 92, 78, 96}, {88, 91, 84, 87}}
// range 遍历:外层遍历行,内层遍历每行的元素
fmt.Println("Grades:")
for i, row := range grades {
fmt.Printf("Student %d: ", i+1)
for _, grade := range row {
fmt.Printf("%d ", grade)
}
fmt.Println()
}
// 求所有元素总和:嵌套 for 循环
var totalSum int
for i := 0; i < len(grades); i++ {
for j := 0; j < len(grades[i]); j++ {
totalSum += grades[i][j]
}
}
fmt.Println("Total sum of grades:", totalSum)
// 获取维度:行数 × 列数
rows := len(grades)
var cols int
if rows > 0 {
cols = len(grades[0])
}
fmt.Printf("Grades array dimensions: %dx%d\n", rows, cols)
}
执行结果:
yaml
Grades:
Student 1: 85 92 78 96
Student 2: 88 91 84 87
Total sum of grades: 701
Grades array dimensions: 2x4
要点:
for i, row := range grades---row是一整行(类型[4]int),i是行号for _, grade := range row--- 遍历每行的每个成绩- 求和:嵌套 for 遍历所有元素,
grades[i][j]用双重索引访问 - 维度:
len(grades)= 行数 = 2,len(grades[0])= 列数 = 4 - 注意:
grades的类型是[2][4]int,所以所有行的列数固定为4(数组长度是类型的一部分)
3. 数组排序 --- sort 包
Go 的 sort 包提供排序函数,但只能对切片 排序,需要用 [:] 把数组转为切片:
go
package main
import (
"fmt"
"sort"
)
func main() {
numbers := [5]int{64, 34, 25, 12, 22}
names := [4]string{"Charlie", "Alice", "Bob", "David"}
prices := [6]float64{19.99, 9.99, 29.99, 4.99, 39.99, 14.99}
fmt.Println("Before sorting:")
fmt.Println("Numbers:", numbers)
fmt.Println("Names:", names)
fmt.Println("Prices:", prices)
// sort.Ints() 排序 int 切片(升序)
sort.Ints(numbers[:]) // numbers[:] 把数组转为切片
// sort.Strings() 排序 string 切片(升序)
sort.Strings(names[:])
// sort.Float64s() 排序 float64 切片(升序)
sort.Float64s(prices[:])
fmt.Println("\nAfter sorting:")
fmt.Println("Numbers:", numbers)
fmt.Println("Names:", names)
fmt.Println("Prices:", prices)
// 检查是否已排序
isSorted := sort.IntsAreSorted(numbers[:])
fmt.Println("Numbers array is sorted:", isSorted)
}
执行结果:
yaml
Before sorting:
Numbers: [64 34 25 12 22]
Names: [Charlie Alice Bob David]
Prices: [19.99 9.99 29.99 4.99 39.99 14.99]
After sorting:
Numbers: [12 22 25 34 64]
Names: [Alice Bob Charlie David]
Prices: [4.99 9.99 14.99 19.99 29.99 39.99]
Numbers array is sorted: true
要点:
sort.Ints(numbers[:])---numbers[:]把[5]int数组转为[]int切片(切片引用数组的底层内存)- 排序后数组本身也被修改了!因为切片和数组共享同一块内存
sort.Ints--- int 升序排序,sort.Strings--- string 按字典序,sort.Float64s--- float64 升序sort.IntsAreSorted(numbers[:])--- 检查切片是否已排序,返回 bool- 数组不能直接排序:
sort.Ints(numbers)编译报错,必须转为切片
4. 逆序排序 --- sort.Reverse
sort.Sort(sort.Reverse(sort.IntSlice(slice))) 实现降序排序:
go
package main
import (
"fmt"
"sort"
)
func main() {
descNumbers := [5]int{1, 2, 3, 4, 5}
fmt.Println("Before reverse sort:", descNumbers)
// 降序排序:sort.Sort + sort.Reverse + sort.IntSlice
sort.Sort(sort.Reverse(sort.IntSlice(descNumbers[:])))
fmt.Println("After reverse sort:", descNumbers)
}
执行结果:
lua
Before reverse sort: [1 2 3 4 5]
After reverse sort: [5 4 3 2 1]
要点:
sort.IntSlice(descNumbers[:])--- 把切片包装为IntSlice类型(实现 sort.Interface 接口)sort.Reverse(...)--- 反转排序顺序(把升序变为降序)sort.Sort(...)--- 执行自定义排序- 三层包装:
sort.Sort(sort.Reverse(sort.IntSlice(slice))) - 同理:
sort.Reverse(sort.StringSlice(slice))--- string 降序 sort.Reverse(sort.Float64Slice(slice))--- float64 降序- 排序同样修改原数组(切片与数组共享内存)
5. 线性搜索 --- 逐个遍历查找
线性搜索从头到尾逐个比较,适用于未排序的数组,时间复杂度 O(n):
go
package main
import "fmt"
func main() {
numbers := [10]int{64, 34, 25, 12, 22, 11, 90, 88, 76, 50}
target := 22
foundIndex := -1 // -1 表示未找到
for i, value := range numbers {
if value == target {
foundIndex = i
break // 找到后立即退出
}
}
if foundIndex != -1 {
fmt.Printf("Linear search: Found %d at index %d\n", target, foundIndex)
} else {
fmt.Printf("Linear search: %d not found\n", target)
}
}
执行结果:
perl
Linear search: Found 22 at index 4
要点:
foundIndex = -1--- 用 -1 表示"未找到"(索引不会是负数)range numbers遍历每个元素,value == target比较是否匹配break--- 找到目标后立即停止遍历,不必检查剩余元素- 线性搜索适用于任何数组(排序或未排序),但效率较低:n 个元素最多比较 n 次
- Go 没有内置的线性搜索函数,需要手动实现
6. 二分搜索 --- sort.SearchInts
二分搜索只在已排序数组上有效,时间复杂度 O(log n):
go
package main
import (
"fmt"
"sort"
)
func main() {
sortedNumbers := [8]int{5, 12, 23, 34, 45, 67, 78, 89}
binaryTarget := 45
// sort.SearchInts 在已排序切片中查找目标
binaryIndex := sort.SearchInts(sortedNumbers[:], binaryTarget)
// 必须验证:SearchInts 返回的可能是"应插入的位置",而非找到的位置
if binaryIndex < len(sortedNumbers) && sortedNumbers[binaryIndex] == binaryTarget {
fmt.Printf("Binary search: Found %d at index %d\n", binaryTarget, binaryIndex)
} else {
fmt.Printf("Binary search: %d not found\n", binaryTarget)
}
}
执行结果:
sql
Binary search: Found 45 at index 4
要点:
sort.SearchInts(sortedNumbers[:], 45)--- 在已排序切片中二分查找- 返回值含义:如果找到,返回目标索引;如果未找到,返回目标应插入的位置
- 所以必须验证:
binaryIndex < len(sortedNumbers) && sortedNumbers[binaryIndex] == binaryTarget - 如果不验证,未找到时也会返回一个索引(插入位置),可能误判为"找到"
- 类似函数:
sort.SearchStrings(slice, target)、sort.SearchFloat64s(slice, target) - 前提:数组必须已排序!在未排序数组上使用二分搜索结果不正确
- 效率:8个元素最多比较3次(log₂8=3),远优于线性搜索
7. 搜索进阶 --- 最值、计数、收集索引
go
package main
import "fmt"
func main() {
numbers := [10]int{64, 34, 25, 12, 22, 11, 90, 88, 76, 50}
// 求最小值和最大值(同时)
min := numbers[0]
max := numbers[0]
minIndex := 0
maxIndex := 0
for i, value := range numbers {
if value < min {
min = value
minIndex = i
}
if value > max {
max = value
maxIndex = i
}
}
fmt.Printf("Minimum: %d at index %d\n", min, minIndex)
fmt.Printf("Maximum: %d at index %d\n", max, maxIndex)
// 计数:统计某值出现的次数
countTarget := 12
count := 0
for _, value := range numbers {
if value == countTarget {
count++
}
}
fmt.Printf("Number %d appears %d times\n", countTarget, count)
// 收集所有匹配的索引
searchValue := 34
var indices []int
for i, value := range numbers {
if value == searchValue {
indices = append(indices, i)
}
}
fmt.Printf("Value %d found at indices: %v\n", searchValue, indices)
}
执行结果:
perl
Minimum: 11 at index 5
Maximum: 90 at index 6
Number 12 appears 1 times
Value 34 found at indices: [1]
要点:
- 求最值:从
numbers[0]开始,逐个比较更新 min/max 和对应的索引 - 计数:遍历所有元素,匹配目标值时
count++ - 收集索引:
var indices []int创建空切片,append(indices, i)追加匹配的索引 %v--- 切片的默认格式输出:[1]- 这些操作都是 O(n) 线性扫描,无法用二分搜索优化(除非需要的是排序后的位置)
- 实际场景:数据分析、过滤、统计等
知识点总结
| 知识点 | 关键概念 |
|---|---|
| 多维数组 | [3][3]int --- "数组的数组",len 取行数和列数 |
| 多维数组遍历 | 嵌套 for/range,外层遍历行,内层遍历每行元素 |
| sort 包排序 | sort.Ints(slice) --- 数组需用 [:] 转切片,排序修改原数组 |
| 逆序排序 | sort.Sort(sort.Reverse(sort.IntSlice(slice))) --- 三层包装 |
| 线性搜索 | 逐个比较 O(n),适用于未排序数组 |
| 二分搜索 | sort.SearchInts O(log n),只适用于已排序数组,需验证结果 |
| 最值/计数/索引收集 | 遍历统计,append 收集匹配索引 |