【发布时间】:2024-01-24 12:26:01
【问题描述】:
【问题讨论】:
【问题讨论】:
binary_search 假定元素按从小到大的顺序排序。你的反了,所以你可以用binary_search_by:
let x = 1; //value to look for
let data = [3,2,1,1,1,0];
let idx = data.binary_search_by(|probe| probe.cmp(x).reverse());
现在,正如你所说,你没有得到第一个。这是意料之中的,因为二分搜索算法将选择一个与所搜索的值相等的任意值。来自文档:
如果有多个匹配项,则可以返回任何一个匹配项。
这很容易用循环解决:
let mut idx = data.binary_search_by(|probe| probe.cmp(&x).reverse());
if let Ok(ref mut i) = idx {
while x > 0 {
if data[*i - 1] != x {
break;
}
*i -= 1;
}
}
但是,如果您期望许多重复项可能会否定二分查找的优势。
如果这对您来说是个问题,您可以尝试变得更聪明。例如,您可以利用 binary_search 文档中的这条评论:
如果未找到该值,则返回
Result::Err,其中包含可以插入匹配元素的索引,同时保持排序顺序。
因此,要使用1 获取第一个值的索引,您需要在2 和1 之间寻找一个虚值(请记住,您的数组是相反的),类似于1.5。这可以通过修改比较函数来完成:
let mut idx = data.binary_search_by(|probe| {
//the 1s in the slice are greater than the 1 in x
probe.cmp(&x).reverse().then(std::cmp::Greater)
});
有一个方便的函数 Ordering::then() 可以满足我们的需要(Rust 标准库非常完整)。
或者你可以使用更简单的直接比较:
let idx = data.binary_search_by(|probe| {
use std::cmp::Ordering::*;
if *probe > x { Less } else { Greater }
});
剩下的唯一细节是这个函数将始终返回Err(i),即i,或者是第一个1的位置,或者是1所在的位置,如果没有的话。额外的比较是必要的,所以解决这个歧义:
if let Err(i) = idx {
//beware! i may be 1 past the end of the slice
if data.get(i) == Some(&x) {
idx = Ok(i);
}
}
【讨论】:
从 1.52.0 开始,[T] 具有 partition_point 方法,可以在 O(log N) 时间内找到带有谓词的分区点。
在你的情况下,应该是:
let xs = vec![3, 2, 1, 1, 1, 0];
let idx = xs.partition_point(|&a| a > 1);
if idx < xs.len() && xs[idx] == 1 {
println!("Found first 1 idx: {}", idx);
}
【讨论】:
if xs.get(idx) == Some(&1) 更短。