Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the lower bound and upper bound of an element in a BTreeSet?

Tags:

rust

Reading the BTreeSet documentation, I can't seem to figure out how to get the least value greater than, or greatest value less than an element from a BTreeSet in logarithmic time.

I see there is a range method that can give the values in an arbitrary (min, max) range, but what if I don't know the range and I just want the previous and/or the next element in logarithmic time?

This would be similar to lower_bound and upper_bound in std::set in C++.

like image 440
suyash Avatar asked Feb 02 '18 04:02

suyash


People also ask

How do you find the upper bound of an array?

Get the value of upper bound: int index = upper_bound(size, size+27, sum) - size; cout << size[index] << endl; I tested performance using the code below. Each one-line code under a for loop give the index where upper bound is.

How do you find the lower bound of a set in C++?

Set lower_bound() function in C++ STL returns an iterator pointing to the element in the container which is equivalent to k passed in the parameter. If k is not present in the set container, then the function returns an iterator pointing to the immediate next element which is just greater than k.

What is lower bound and upper bound in array?

The index of the first element of an array is called its lower bound, while the index of the last element is called its upper bound. By default, an array is indexed beginning with zero. The declaration must include the number of elements inside parentheses.


2 Answers

but what if I don't know the range

Then use an unbounded range:

use std::collections::BTreeSet;

fn neighbors(tree: &BTreeSet<i32>, val: i32) -> (Option<&i32>, Option<&i32>) {
    use std::ops::Bound::*;

    let mut before = tree.range((Unbounded, Excluded(val)));
    let mut after = tree.range((Excluded(val), Unbounded));

    (before.next_back(), after.next())
}

fn main() {
    let tree: BTreeSet<_> = [1, 3, 5].iter().cloned().collect();

    let (prev, next) = neighbors(&tree, 2);

    println!("greatest less than 2: {:?}", prev);
    println!("least bigger than 2:  {:?}", next);
}
greatest less than 2: Some(1)
least bigger than 2:  Some(3)

BTreeSet::range returns a double-ended iterator, so you can pull from either side of it.

Note that we are using the very explicit Bound operator so that we do not include the value we are looking around.


There have been discussions about enhancing BTreeMap / BTreeSet to have a "cursor" API that might allow you to find an element and then "move around" inside the tree. This would allow you to avoid searching through the tree to find the start node twice, but it has not been implemented.

A pull request was opened to do so, but it was closed because it was deemed that there should be more discussion about how such an API should look and work.

like image 110
Shepmaster Avatar answered Oct 19 '22 20:10

Shepmaster


Well... if you don't mind modifying the current collection and taking a performance hit... it appears that you can use split_off creatively.

let mut tree = BTreeSet::new();
tree.insert(1);
tree.insert(3);
tree.insert(5);

let other = tree.split_off(&2);

println!("{:?}", tree);
println!("{:?}", other);

Will print {1} and {3, 5}:

  • the lower-bound is the first element of the second range,
  • the upper-bound is the first element of the second range if not equal, and the second otherwise.

Once you are done, you can reassemble the tree using tree.append(other).


And yes, it's really less than ideal...

like image 2
Matthieu M. Avatar answered Oct 19 '22 19:10

Matthieu M.