ZetCode

Golang slices.BinarySearchFunc

最后修改于 2025 年 4 月 20 日

本教程将介绍如何在 Go 语言中使用 slices.BinarySearchFunc 函数。我们将涵盖使用自定义比较函数进行二分查找操作。

slices.BinarySearchFunc 函数在已排序的 slice 上执行二分查找。它使用自定义比较函数来确定元素的排序。

此函数对于在大型已排序集合中进行搜索非常高效。它会返回目标值在保持顺序的情况下应该插入的位置。

基本的 BinarySearchFunc 示例

最简单的用法是在已排序的 slice 中搜索数字。我们定义一个返回 -1、0 或 1 的比较函数。

basic_search.go
package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{1, 3, 5, 7, 9}
    
    cmp := func(a, b int) int {
        if a == b {
            return 0
        } else if a < b {
            return -1
        }
        return 1
    }
    
    pos, found := slices.BinarySearchFunc(numbers, 5, cmp)
    fmt.Printf("Position: %d, Found: %v\n", pos, found)
}

比较函数遵循标准的排序规则。搜索在 slice 的第 2 个位置找到值 5。

搜索字符串

slices.BinarySearchFunc 可以搜索字符串 slice。此示例执行不区分大小写的字符串比较。

string_search.go
package main

import (
    "fmt"
    "slices"
    "strings"
)

func main() {
    words := []string{"apple", "Banana", "cherry", "Date"}
    
    cmp := func(a, b string) int {
        aLower := strings.ToLower(a)
        bLower := strings.ToLower(b)
        
        switch {
        case aLower == bLower:
            return 0
        case aLower < bLower:
            return -1
        default:
            return 1
        }
    }
    
    pos, found := slices.BinarySearchFunc(words, "banana", cmp)
    fmt.Printf("Position: %d, Found: %v\n", pos, found)
}

比较函数首先将字符串转换为小写。这使得在查找“Banana”时搜索不区分大小写。

按字段搜索结构体

我们可以按特定字段搜索结构体 slice。此示例按年龄搜索人员。

struct_search.go
package main

import (
    "fmt"
    "slices"
)

type Person struct {
    Name string
    Age  int
}

func main() {
    people := []Person{
        {"Alice", 25},
        {"Bob", 30},
        {"Charlie", 35},
    }
    
    cmp := func(p Person, age int) int {
        switch {
        case p.Age == age:
            return 0
        case p.Age < age:
            return -1
        default:
            return 1
        }
    }
    
    pos, found := slices.BinarySearchFunc(people, 30, cmp)
    fmt.Printf("Position: %d, Found: %v\n", pos, found)
}

比较函数会比较 Person 结构体的 Age 字段。它在第 1 个位置找到 Bob,他的年龄正好是 30 岁。

自定义排序

BinarySearchFunc 支持自定义排序方案。此示例按降序搜索。

descending_order.go
package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{9, 7, 5, 3, 1} // Descending order
    
    cmp := func(a, b int) int {
        if a == b {
            return 0
        } else if a > b { // Note: reversed comparison
            return -1
        }
        return 1
    }
    
    pos, found := slices.BinarySearchFunc(numbers, 5, cmp)
    fmt.Printf("Position: %d, Found: %v\n", pos, found)
}

比较函数会颠倒标准的排序逻辑。这允许在降序排序的 slice 中进行搜索。

带有部分匹配的搜索

我们可以在比较中实现部分匹配。此示例在字符串 slice 中搜索前缀。

prefix_search.go
package main

import (
    "fmt"
    "slices"
    "strings"
)

func main() {
    words := []string{"apple", "application", "banana", "book"}
    
    cmp := func(s, prefix string) int {
        if strings.HasPrefix(s, prefix) {
            return 0
        } else if s < prefix {
            return -1
        }
        return 1
    }
    
    pos, found := slices.BinarySearchFunc(words, "app", cmp)
    fmt.Printf("Position: %d, Found: %v\n", pos, found)
}

比较使用 strings.HasPrefix 检查前缀匹配。它找到“app”的匹配项“apple”和“application”。

处理未找到的情况

当未找到目标时,该函数返回插入位置。此示例演示了如何处理缺失的元素。

not_found.go
package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{10, 20, 30, 40, 50}
    
    cmp := func(a, b int) int {
        if a == b {
            return 0
        } else if a < b {
            return -1
        }
        return 1
    }
    
    pos, found := slices.BinarySearchFunc(numbers, 25, cmp)
    if found {
        fmt.Println("Found at position", pos)
    } else {
        fmt.Printf("Not found, would insert at %d\n", pos)
    }
}

值 25 不在 slice 中,但将被插入到第 2 个位置。这会保持 slice 的排序顺序。

实际示例:字典查找

这个实际示例实现了字典查找。它在一个已排序的 slice 中搜索单词定义。

dictionary.go
package main

import (
    "fmt"
    "slices"
)

type Entry struct {
    Word       string
    Definition string
}

func main() {
    dictionary := []Entry{
        {"apple", "A fruit"},
        {"banana", "Yellow fruit"},
        {"cherry", "Small red fruit"},
    }
    
    cmp := func(e Entry, word string) int {
        switch {
        case e.Word == word:
            return 0
        case e.Word < word:
            return -1
        default:
            return 1
        }
    }
    
    pos, found := slices.BinarySearchFunc(dictionary, "banana", cmp)
    if found {
        fmt.Println("Definition:", dictionary[pos].Definition)
    } else {
        fmt.Println("Word not found")
    }
}

比较函数按 Word 字段匹配 Entry 结构体。它有效地在已排序的字典 slice 中查找定义。

来源

Go 实验性切片包文档

本教程介绍了 Go 语言中的 slices.BinarySearchFunc 函数。我们通过自定义比较函数探索了各种搜索场景。

作者

我的名字是 Jan Bodnar,我是一名充满激情的程序员,拥有丰富的编程经验。我自 2007 年以来一直撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出所有 Go 教程