<返回目录     Powered by claud/xia兄

第5课: 数组与切片

数组(Array)

数组是固定长度的相同类型元素序列。数组的长度是类型的一部分。

数组声明与初始化

package main

import "fmt"

func main() {
    // 声明数组
    var arr [5]int
    fmt.Println(arr)  // [0 0 0 0 0]

    // 声明并初始化
    var nums [3]int = [3]int{1, 2, 3}
    fmt.Println(nums)  // [1 2 3]

    // 简短声明
    colors := [4]string{"red", "green", "blue", "yellow"}
    fmt.Println(colors)

    // 自动推断长度
    auto := [...]int{1, 2, 3, 4, 5}
    fmt.Println(len(auto))  // 5

    // 指定索引初始化
    indexed := [5]int{1: 10, 3: 30}
    fmt.Println(indexed)  // [0 10 0 30 0]
}

访问和修改数组元素

func main() {
    arr := [5]int{1, 2, 3, 4, 5}

    // 访问元素
    fmt.Println(arr[0])  // 1
    fmt.Println(arr[4])  // 5

    // 修改元素
    arr[2] = 100
    fmt.Println(arr)  // [1 2 100 4 5]

    // 获取长度
    fmt.Println(len(arr))  // 5

    // 遍历数组
    for i := 0; i < len(arr); i++ {
        fmt.Printf("arr[%d] = %d\n", i, arr[i])
    }

    // 使用range遍历
    for index, value := range arr {
        fmt.Printf("索引: %d, 值: %d\n", index, value)
    }
}

多维数组

func main() {
    // 二维数组
    var matrix [3][3]int = [3][3]int{
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9},
    }

    // 访问元素
    fmt.Println(matrix[1][2])  // 6

    // 遍历二维数组
    for i := 0; i < len(matrix); i++ {
        for j := 0; j < len(matrix[i]); j++ {
            fmt.Printf("%d ", matrix[i][j])
        }
        fmt.Println()
    }

    // 使用range遍历
    for i, row := range matrix {
        for j, val := range row {
            fmt.Printf("matrix[%d][%d] = %d\n", i, j, val)
        }
    }
}

数组作为函数参数

// 数组是值类型,传递时会复制整个数组
func modifyArray(arr [5]int) {
    arr[0] = 100  // 不会影响原数组
}

func main() {
    nums := [5]int{1, 2, 3, 4, 5}
    modifyArray(nums)
    fmt.Println(nums)  // [1 2 3 4 5] 未改变

    // 使用指针传递
    modifyArrayPtr(&nums)
    fmt.Println(nums)  // [100 2 3 4 5] 已改变
}

func modifyArrayPtr(arr *[5]int) {
    arr[0] = 100  // 会影响原数组
}

切片(Slice)

切片是对数组的抽象,提供动态大小的灵活视图。切片是引用类型。

切片的创建

package main

import "fmt"

func main() {
    // 方式1:从数组创建切片
    arr := [5]int{1, 2, 3, 4, 5}
    slice1 := arr[1:4]  // [2 3 4]
    fmt.Println(slice1)

    // 方式2:直接声明切片
    slice2 := []int{1, 2, 3, 4, 5}
    fmt.Println(slice2)

    // 方式3:使用make创建
    slice3 := make([]int, 5)      // 长度5,容量5
    slice4 := make([]int, 5, 10)  // 长度5,容量10
    fmt.Println(slice3, slice4)

    // 方式4:nil切片
    var slice5 []int
    fmt.Println(slice5 == nil)  // true
}

切片的长度和容量

func main() {
    slice := make([]int, 5, 10)

    fmt.Println("长度:", len(slice))  // 5
    fmt.Println("容量:", cap(slice))  // 10

    // 切片操作
    arr := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

    s1 := arr[2:5]  // [2 3 4]
    fmt.Printf("s1: %v, len=%d, cap=%d\n", s1, len(s1), cap(s1))
    // s1: [2 3 4], len=3, cap=8

    s2 := arr[:5]   // [0 1 2 3 4]
    fmt.Printf("s2: %v, len=%d, cap=%d\n", s2, len(s2), cap(s2))
    // s2: [0 1 2 3 4], len=5, cap=10

    s3 := arr[5:]   // [5 6 7 8 9]
    fmt.Printf("s3: %v, len=%d, cap=%d\n", s3, len(s3), cap(s3))
    // s3: [5 6 7 8 9], len=5, cap=5

    s4 := arr[:]    // [0 1 2 3 4 5 6 7 8 9]
    fmt.Printf("s4: %v, len=%d, cap=%d\n", s4, len(s4), cap(s4))
}

append添加元素

func main() {
    // 添加单个元素
    slice := []int{1, 2, 3}
    slice = append(slice, 4)
    fmt.Println(slice)  // [1 2 3 4]

    // 添加多个元素
    slice = append(slice, 5, 6, 7)
    fmt.Println(slice)  // [1 2 3 4 5 6 7]

    // 合并切片
    slice2 := []int{8, 9, 10}
    slice = append(slice, slice2...)
    fmt.Println(slice)  // [1 2 3 4 5 6 7 8 9 10]

    // append会自动扩容
    s := make([]int, 0, 2)
    fmt.Printf("len=%d, cap=%d\n", len(s), cap(s))  // len=0, cap=2

    s = append(s, 1)
    fmt.Printf("len=%d, cap=%d\n", len(s), cap(s))  // len=1, cap=2

    s = append(s, 2)
    fmt.Printf("len=%d, cap=%d\n", len(s), cap(s))  // len=2, cap=2

    s = append(s, 3)  // 触发扩容
    fmt.Printf("len=%d, cap=%d\n", len(s), cap(s))  // len=3, cap=4
}

copy复制切片

func main() {
    src := []int{1, 2, 3, 4, 5}
    dst := make([]int, len(src))

    // 复制切片
    n := copy(dst, src)
    fmt.Println("复制了", n, "个元素")
    fmt.Println("dst:", dst)  // [1 2 3 4 5]

    // 修改dst不影响src
    dst[0] = 100
    fmt.Println("src:", src)  // [1 2 3 4 5]
    fmt.Println("dst:", dst)  // [100 2 3 4 5]

    // 部分复制
    src2 := []int{10, 20, 30, 40, 50}
    dst2 := make([]int, 3)
    copy(dst2, src2)
    fmt.Println(dst2)  // [10 20 30]

    // 复制到更大的切片
    dst3 := make([]int, 10)
    copy(dst3, src2)
    fmt.Println(dst3)  // [10 20 30 40 50 0 0 0 0 0]
}

切片的删除操作

func main() {
    slice := []int{1, 2, 3, 4, 5}

    // 删除索引为2的元素
    index := 2
    slice = append(slice[:index], slice[index+1:]...)
    fmt.Println(slice)  // [1 2 4 5]

    // 删除第一个元素
    slice = slice[1:]
    fmt.Println(slice)  // [2 4 5]

    // 删除最后一个元素
    slice = slice[:len(slice)-1]
    fmt.Println(slice)  // [2 4]
}

// 通用删除函数
func remove(slice []int, index int) []int {
    return append(slice[:index], slice[index+1:]...)
}

切片的插入操作

func main() {
    slice := []int{1, 2, 4, 5}

    // 在索引2处插入3
    index := 2
    value := 3
    slice = append(slice[:index], append([]int{value}, slice[index:]...)...)
    fmt.Println(slice)  // [1 2 3 4 5]
}

// 通用插入函数
func insert(slice []int, index int, value int) []int {
    slice = append(slice, 0)
    copy(slice[index+1:], slice[index:])
    slice[index] = value
    return slice
}

切片的底层原理

// 切片的内部结构
type slice struct {
    array unsafe.Pointer  // 指向底层数组的指针
    len   int             // 切片长度
    cap   int             // 切片容量
}

func main() {
    // 切片共享底层数组
    arr := [5]int{1, 2, 3, 4, 5}
    s1 := arr[1:4]  // [2 3 4]
    s2 := arr[2:5]  // [3 4 5]

    // 修改s1会影响s2
    s1[1] = 100
    fmt.Println(s1)  // [2 100 4]
    fmt.Println(s2)  // [100 4 5]
    fmt.Println(arr) // [1 2 100 4 5]
}

综合示例

示例1:动态数组实现

package main

import "fmt"

type DynamicArray struct {
    data []int
}

func NewDynamicArray() *DynamicArray {
    return &DynamicArray{
        data: make([]int, 0),
    }
}

func (da *DynamicArray) Append(value int) {
    da.data = append(da.data, value)
}

func (da *DynamicArray) Get(index int) int {
    if index < 0 || index >= len(da.data) {
        panic("索引越界")
    }
    return da.data[index]
}

func (da *DynamicArray) Remove(index int) {
    if index < 0 || index >= len(da.data) {
        panic("索引越界")
    }
    da.data = append(da.data[:index], da.data[index+1:]...)
}

func (da *DynamicArray) Size() int {
    return len(da.data)
}

func main() {
    arr := NewDynamicArray()
    arr.Append(1)
    arr.Append(2)
    arr.Append(3)
    fmt.Println(arr.data)  // [1 2 3]

    arr.Remove(1)
    fmt.Println(arr.data)  // [1 3]
}

示例2:切片排序

package main

import (
    "fmt"
    "sort"
)

func main() {
    // 整数切片排序
    nums := []int{5, 2, 8, 1, 9, 3}
    sort.Ints(nums)
    fmt.Println(nums)  // [1 2 3 5 8 9]

    // 字符串切片排序
    strs := []string{"banana", "apple", "cherry"}
    sort.Strings(strs)
    fmt.Println(strs)  // [apple banana cherry]

    // 自定义排序
    people := []struct {
        Name string
        Age  int
    }{
        {"Alice", 25},
        {"Bob", 20},
        {"Charlie", 30},
    }

    sort.Slice(people, func(i, j int) bool {
        return people[i].Age < people[j].Age
    })

    fmt.Println(people)
}

示例3:切片去重

func removeDuplicates(slice []int) []int {
    seen := make(map[int]bool)
    result := []int{}

    for _, value := range slice {
        if !seen[value] {
            seen[value] = true
            result = append(result, value)
        }
    }

    return result
}

func main() {
    nums := []int{1, 2, 2, 3, 4, 4, 5}
    unique := removeDuplicates(nums)
    fmt.Println(unique)  // [1 2 3 4 5]
}

示例4:切片反转

func reverse(slice []int) {
    for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 {
        slice[i], slice[j] = slice[j], slice[i]
    }
}

func main() {
    nums := []int{1, 2, 3, 4, 5}
    reverse(nums)
    fmt.Println(nums)  // [5 4 3 2 1]
}

示例5:切片过滤

func filter(slice []int, fn func(int) bool) []int {
    result := []int{}
    for _, value := range slice {
        if fn(value) {
            result = append(result, value)
        }
    }
    return result
}

func main() {
    nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

    // 过滤偶数
    evens := filter(nums, func(n int) bool {
        return n%2 == 0
    })
    fmt.Println(evens)  // [2 4 6 8 10]

    // 过滤大于5的数
    greaterThan5 := filter(nums, func(n int) bool {
        return n > 5
    })
    fmt.Println(greaterThan5)  // [6 7 8 9 10]
}
数组 vs 切片对比:
特性 数组 切片
长度 固定 动态
类型 值类型 引用类型
传递 复制整个数组 复制切片头(指针、长度、容量)
使用场景 固定大小的数据 动态大小的数据

实践练习

  1. 编写函数,找出切片中的最大值和最小值
  2. 实现切片的旋转操作(左旋或右旋k位)
  3. 编写函数,合并两个有序切片为一个有序切片
  4. 实现切片的分组功能(按指定大小分组)
  5. 编写函数,计算两个切片的交集、并集和差集
  6. 实现一个栈结构(使用切片)
  7. 实现一个队列结构(使用切片)
  8. 编写函数,将二维切片转置
常见陷阱: