【问题标题】:Is there an efficient function in Rust that finds the index of the first occurrence of a value in a sorted vector?Rust 中是否有一个有效的函数可以找到排序向量中第一次出现的值的索引?
【发布时间】:2024-01-24 12:26:01
【问题描述】:

[3, 2, 1, 1, 1, 0]中,如果我们要搜索的值是1,那么函数应该返回2。

我找到了binary search,但它似乎返回了最后一次出现。

我不想要一个遍历整个向量并一一匹配的函数。

【问题讨论】:

    标签: rust slice


    【解决方案1】:

    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 获取第一个值的索引,您需要在21 之间寻找一个虚值(请记住,您的数组是相反的),类似于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);
        }
    }
    

    【讨论】:

      【解决方案2】:

      从 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(&amp;1) 更短。
      最近更新 更多