英文:
Go binary search error
问题
假设你有两个使用案例:
a := []int{2, 2, 3, 4}
i := sort.Search(len(a), func(pos int) bool { return a[pos] == 2 })
fmt.Printf("%v -> %v \n", a, i)
b := []int{1, 2, 2, 3, 4}
j := sort.Search(len(b), func(pos int) bool { return b[pos] == 2 })
fmt.Printf("%v -> %v \n", b, j)
这两个案例的答案是:
[2 2 3 4] -> 4
[1 2 2 3 4] -> 1
我认为在这两种情况下都应该是 1
,是吗?有人知道为什么吗?
英文:
Suppose you have two use cases:
a := [] int {2, 2, 3, 4}
i := sort.Search(len(a), func(pos int) bool { return a[pos] == 2})
fmt.Printf("%v -> %v \n", a, i)
b := [] int {1, 2, 2, 3, 4}
j := sort.Search(len(b), func(pos int) bool { return b[pos] == 2})
fmt.Printf("%v -> %v \n", b, j)
Answer for those are:
[2 2 3 4] -> 4
[1 2 2 3 4] -> 1
I suppose it must be 1
in both cases, no? Does anyone knows why?
答案1
得分: 4
sort.Search()
函数返回在 [0, n)
范围内满足 f(i)
为 true
的最小索引 i
。
由于切片索引是从 0 开始的,所以你应该期望第一个返回值为 0
,第二个返回值为 1
。第二个例子(看起来)是正确的。
引用文档中的说明:
> ...假设在范围 [0, n)
上,f(i) == true
意味着 f(i+1) == true
。也就是说,Search
要求 f
在输入范围 [0, n)
的某个(可能为空)前缀上为 false
,然后在剩余部分(可能为空)上为 true
;Search
返回第一个为 true
的索引。如果没有这样的索引,Search
返回 n
。
你的函数不符合 sort.Search()
的要求。你的函数不是用来判断指定索引处的元素是否是你要找的元素,而是用来判断:
- 如果请求的索引处的元素大于或等于搜索的元素,则返回
true
- 如果请求的索引处的元素小于搜索的元素,则返回
false
(如果你只告诉它们相等而不告诉它们大于或小于,就无法进行二分查找。)
所以只需使用 >=
比较而不是 ==
。正确的用法如下:
a := []int{2, 2, 3, 4}
i := sort.Search(len(a), func(pos int) bool { return a[pos] >= 2 })
fmt.Printf("%v -> %v \n", a, i)
b := []int{1, 2, 2, 3, 4}
j := sort.Search(len(b), func(pos int) bool { return b[pos] >= 2 })
fmt.Printf("%v -> %v \n", b, j)
输出结果(在 Go Playground 上尝试):
[2 2 3 4] -> 0
[1 2 2 3 4] -> 1
注意:文档中还提到:
> 例如,给定一个按升序排序的切片数据,调用
>
> Search(len(data), func(i int) bool { return data[i] >= 23 })
>
> 返回最小的索引 i
,使得 data[i] >= 23
。如果调用者想要找到 23
是否在切片中,它必须单独测试 data[i] == 23
。
更简单的替代方法:sort.SearchInts()
如果你想在 int
切片([]int
)中搜索一个 int
值,可以直接使用 sort.SearchInts()
:
a := []int{2, 2, 3, 4}
i := sort.SearchInts(a, 2)
fmt.Printf("%v -> %v \n", a, i)
b := []int{1, 2, 2, 3, 4}
j := sort.SearchInts(b, 2)
fmt.Printf("%v -> %v \n", b, j)
输出结果(在 Go Playground 上尝试):
[2 2 3 4] -> 0
[1 2 2 3 4] -> 1
英文:
sort.Search()
returns the smallest index i
in [0, n)
at which f(i)
is true
.
Since slice indices are 0-based, you should expect a return of 0
for the first and 1
for the second. It (seemingly) works for the second.
Quoting from the doc:
> ...assuming that on the range [0, n), f(i) == true
implies f(i+1) == true
. That is, Search requires that f
is false
for some (possibly empty) prefix of the input range [0, n) and then true
for the (possibly empty) remainder; Search returns the first true
index. If there is no such index, Search returns n
.
Your function does not meet the criteria sort.Search()
expects. Your function is not to tell if the element at the specified index is the one you're looking for, but rather to tell:
- if the element at the requested index is equal to or is greater than (return
true
) - or if the element at the specified index is less than the searched one (return
false
).
(A binary search could not be carried out if you would only tell when they are equal but not when they are greater or less.)
So simply use >=
comparison instead of ==
. Correct usage:
a := []int{2, 2, 3, 4}
i := sort.Search(len(a), func(pos int) bool { return a[pos] >= 2 })
fmt.Printf("%v -> %v \n", a, i)
b := []int{1, 2, 2, 3, 4}
j := sort.Search(len(b), func(pos int) bool { return b[pos] >= 2 })
fmt.Printf("%v -> %v \n", b, j)
Output (try it on the <kbd>Go Playground</kbd>):
[2 2 3 4] -> 0
[1 2 2 3 4] -> 1
Note: also from the doc:
> For instance, given a slice data sorted in ascending order, the call
>
> Search(len(data), func(i int) bool { return data[i] >= 23 })
>
> returns the smallest index i
such that data[i] >= 23
. If the caller wants to find whether 23
is in the slice, it must test data[i] == 23
separately.
Easier alternative: sort.SearchInts()
If you want to search for an int
value in an int
slice ([]int
), simply use sort.SearchInts()
:
a := []int{2, 2, 3, 4}
i := sort.SearchInts(a, 2)
fmt.Printf("%v -> %v \n", a, i)
b := []int{1, 2, 2, 3, 4}
j := sort.SearchInts(b, 2)
fmt.Printf("%v -> %v \n", b, j)
Output (try it on the <kbd>Go Playground</kbd>):
[2 2 3 4] -> 0
[1 2 2 3 4] -> 1
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论