使用整数获取映射到数字范围的值,以在Rust中查找键

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用整数获取映射到数字范围的值,以在Rust中查找键相关的知识,希望对你有一定的参考价值。

在Rust中,使用位于该范围内的整数,我可以为映射到一系列数字的值选择最简单的方法是什么?

就像是:

range_values.insert(0..1000, "A");
range_values.insert(1001..2000, "B");

get_value(range_values, 1500); // returns "B"
答案

最简单的方法是使用对向量并按顺序迭代它们以在向量中找到正确的条目。

use std::ops::Range;

fn get_value<'a>(range_values: &[(Range<i32>, &'a str)], key: i32) -> Option<&'a str> {
    for &(ref range, value) in range_values {
        //if range.contains(key) { // unstable as of Rust 1.23.0
        if (range.start <= key) && (key < range.end) {
            return Some(value);
        }
    }

    return None;
}

fn main() {
    let mut range_values = vec![];
    range_values.push((0..1000, "A"));
    range_values.push((1001..2000, "B"));

    println!("{:?}", get_value(&range_values, -1));   // prints None
    println!("{:?}", get_value(&range_values, 0));    // prints Some("A")
    println!("{:?}", get_value(&range_values, 500));  // prints Some("A")
    println!("{:?}", get_value(&range_values, 1000)); // prints None
    println!("{:?}", get_value(&range_values, 1500)); // prints Some("B")
    println!("{:?}", get_value(&range_values, 2500)); // prints None
}

但是,它并不是最有效的方法,尤其是当向量变大时。如果矢量被排序,则执行二分搜索(O(log n))而不是线性扫描(O(n))更有效。这假设范围不重叠。 (如果矢量没有排序,你只使用一次,那么进行线性扫描会更快,因为排序是O(n log n),即比单个线性扫描慢。)

use std::ops::Range;

fn get_value<'a>(range_values: &[(Range<i32>, &'a str)], key: i32) -> Option<&'a str> {
    // Find the index of the first item where `range.start <= key`.
    let index =
        match range_values.binary_search_by_key(&key, |&(ref range, _)| range.start) {
            Ok(index) => Some(index),

            // If the requested key is smaller than the smallest range in the slice,
            // we would be computing `0 - 1`, which would underflow an `usize`.
            // We use `checked_sub` to get `None` instead.
            Err(index) => index.checked_sub(1),
        };

    if let Some(index) = index {
        let (ref range, value) = range_values[index];
        if key < range.end {
            return Some(value);
        }
    }

    return None;
}

fn main() {
    let mut range_values = vec![];
    range_values.push((0..1000, "A"));
    range_values.push((1001..2000, "B"));

    // if the vector is not already sorted:
    range_values.sort_by_key(|&(ref range, _)| range.start);

    println!("{:?}", get_value(&range_values, -1));   // prints None
    println!("{:?}", get_value(&range_values, 0));    // prints Some("A")
    println!("{:?}", get_value(&range_values, 500));  // prints Some("A")
    println!("{:?}", get_value(&range_values, 1000)); // prints None
    println!("{:?}", get_value(&range_values, 1500)); // prints Some("B")
    println!("{:?}", get_value(&range_values, 2500)); // prints None
}
另一答案

Francis Gagné's idea of using binary search类似,我们可以使用BTreeMap用于相同的目的。这样可以更轻松地添加和删除,而无需每次都对列表​​进行重新排序。与其他答案一样,它仅适用于间隔不重叠的情况。

我们需要围绕Range构造一个新类型,以使用范围的下限作为查找的关键。我们还包装BTreeMap以提供更好的访问方法。

#![feature(inclusive_range_syntax)]

use std::collections::BTreeMap;
use std::{borrow, cmp}; 

fn main() {
    let mut range_values = RangeMap::default();

    range_values.insert(0..1000, "A");
    range_values.insert(1001..2000, "B");

    assert_eq!(range_values.get(-1),   None);
    assert_eq!(range_values.get(0),    Some(&"A"));
    assert_eq!(range_values.get(500),  Some(&"A"));
    assert_eq!(range_values.get(1000), None);
    assert_eq!(range_values.get(1500), Some(&"B"));
    assert_eq!(range_values.get(2500), None);
}

#[derive(Debug, Default, Clone)]
struct RangeMap<V>(BTreeMap<RangeKey, V>);

impl<V> RangeMap<V> {
    fn insert(&mut self, r: std::ops::Range<i32>, v: V) {
        self.0.insert(RangeKey(r), v);
    }

    fn get(&self, id: i32) -> Option<&V> {
        self.0.range(..=id).rev().next().and_then(|(k, v)| {
            // Recheck inside range
            if k.contains(id) {
                Some(v)
            } else {
                None
            }
        })
    }
}

#[derive(Debug, Clone, Eq)]
struct RangeKey(std::ops::Range<i32>);

impl RangeKey {
    fn contains(&self, k: i32) -> bool {
        k >= self.0.start && k < self.0.end
    }

    fn key(&self) -> &i32 {
        &self.0.start
    }
}

impl PartialEq for RangeKey {
    fn eq(&self, other: &Self) -> bool {
        self.key().eq(&other.key())
    }
}

impl PartialOrd for RangeKey {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for RangeKey {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.key().cmp(&other.key())
    }
}

impl borrow::Borrow<i32> for RangeKey {
    fn borrow(&self) -> &i32 {
        self.key()
    }
}

不幸的是,这需要不稳定的RangeInclusive

对于更复杂的解决方案,您需要一个interval tree

另一答案

如果数字是从0开始按顺序开始的,那么最简单和最有效的方法就是使用向量:

let mut range_values = vec!["A"; 1000];
range_values.append(&mut vec!["B"; 1000]);
assert_eq!(range_values[1500], "B");

如果它们是顺序但不以0开头,您仍然可以通过简单的翻译来完成:

assert_eq!(range_values[1500 - start], "B");

如果它们不是连续的,那么你需要使用Francis Gagné'sShepmaster's答案。

以上是关于使用整数获取映射到数字范围的值,以在Rust中查找键的主要内容,如果未能解决你的问题,请参考以下文章

使用jquery如何限制输入框输入数字范围在20到30之间

如何在整数数组中查找所有有序元素对,其总和位于给定的值范围内

rust的常见整数类型

Python在列表或数组中查找范围之间的数字

基于范围的数据结构的密钥查找

从 Rust 中的多个音频流中并行获取相同大小的块