5.2 Go 数组进阶学习笔记(多维、排序、搜索)

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 --- 未初始化,所有元素为零值 0
  • matrix[1][1] = 5 --- 先选行(索引1),再选列(索引1),设为5
  • len(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 收集匹配索引
相关推荐
newerp8 小时前
Redis 操作与缓存策略
后端·程序员·go
newerp8 小时前
GORM ORM 基础
后端·程序员·go
newerp9 小时前
CRUD 操作与预处理语句
后端·程序员·go
程序员爱钓鱼12 小时前
Go 编程实战:闭包 Closure——函数如何记住外部变量
后端·google·go
橘bird1 天前
Go 语言学习教程 万字精讲(新手完整版)
go
用户330144867631 天前
08. mcentral:中心缓存的 span 管理
go
用户330144867631 天前
07. mcache.refill:缓存补充与 span 归还
go
程序员爱钓鱼1 天前
Go 编程实战:匿名函数 Anonymous Function——没有名字的函数与灵活回调
后端·面试·go
用户330144867632 天前
03. Size Class 分级机制
go