Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add remove, contains functions to BinaryHeap #66724

Closed
est31 opened this issue Nov 24, 2019 · 10 comments
Closed

Add remove, contains functions to BinaryHeap #66724

est31 opened this issue Nov 24, 2019 · 10 comments
Labels
A-collections Area: std::collections. C-feature-request Category: A feature request, i.e: not implemented / a PR. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@est31
Copy link
Member

est31 commented Nov 24, 2019

BinaryHeap currently only has functions to pop the maximum element and push an arbitrary element to it. There is no function to remove a given element from the heap or to check whether the heap contains an element. Both are kinda common operations.

@jonas-schievink jonas-schievink added A-collections Area: std::collections. C-feature-request Category: A feature request, i.e: not implemented / a PR. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Nov 25, 2019
@hellow554
Copy link
Contributor

But isn't that excactly the purpose of a BinaryHeap. You are most likely only interested in the biggest/smallest element. If you just want an ordered thing, use a BTreeSet which will provide the needed functions.

@est31
Copy link
Member Author

est31 commented Nov 25, 2019

There is no pop function in a BTreeSet although you could probably use iter().next() for the same functionality.

Anyways, there are algorithms where you are interested in the minimum of a set in one step, then you remove it as well as items associated with it (which aren't neccessarily minima themselves). Then you are interested in the minimum again, etc. What I'm doing right now is to have a separate HashSet that I'm adding removed items to, and then simply doing a continue operation in the pop loop if the item is in the removed list. It might be faster than using the BinaryHeap for it, but it's not really a nice design.

For me, the purpose of a BinaryHeap is not that its operations are restricted, but that the invariant of a minimum being quickly accessible is maintained.

@est31
Copy link
Member Author

est31 commented Jan 30, 2020

@billyrieger as you made #68378 , do you want to make this one next?

@billyrieger
Copy link
Contributor

For me, the purpose of a BinaryHeap is not that its operations are restricted, but that the invariant of a minimum being quickly accessible is maintained.

I agree with this sentiment. On the other hand, one could argue adding these operations would unnecessarily clutter the BinaryHeap API. Regardless, I'll take a stab at this.

@coriolinus
Copy link

One use case comes straight from Wikipedia: the A* algorithm specifically suggests using a heap for openSet, and it assumes that .contains is available:

                if neighbor not in openSet
                    openSet.add(neighbor)

IMO .remove and .contains are reasonable operations to add to any container; they are fundamental, and do not clutter the API.

@gowrizrh
Copy link

gowrizrh commented Feb 2, 2021

I am stuck right exactly here, for exactly what @coriolinus mentioned. I'm implementing a number of algorithms from the A* family and there's specifically no .contains in a BinaryHeap which is a priority queue as said by the docs.

    if !open.contains(neighbour) { // this would be great!
        open.push(neighbour);
    }

@Neutron3529
Copy link
Contributor

Neutron3529 commented Feb 17, 2021

    if !open.contains(neighbour) { // this would be great!
        open.push(neighbour);
    }

a fn insert(&mut self, item: T)->bool is more beautiful here.

    if open.insert(neighbour) { 
        // this would be great!
    }

Previously I made a mistake that think insert is as fast and easy to implement as push..
I'm fixing that problem, the PR will be later.(update: not possible until I find a better solution.)
That PR may provide sub-linear time complexity, but slower than push.

Edit: find it is much harder than I could handle (unsafe for better performance, extra space consuming, cache miss(the worst case, we should check every element in the heap in a strange order))

Maybe insert looks better, but .. maybe insert with BTreeSet is better.

@est31
Copy link
Member Author

est31 commented Mar 3, 2021

I think I'll close this because such functions would have bad (linear) performance characteristics: #82001 (comment)

@est31 est31 closed this as completed Mar 3, 2021
@thisisrandy
Copy link

This is the top Google hit for "rust BinaryHeap contains" and the top DuckDuckGo hit under github.com, so in case anyone comes here searching for a way to test BinaryHeap membership, please note that you can do so using iter/any as in my_heap.iter().any(|heap_item| heap_item == test_item).

@est31
Copy link
Member Author

est31 commented Jun 29, 2023

With as_slice (unstable, see #83659), you can also do my_heap.as_slice().contains(needle). contains is also always linear time, but it has some optimized implementations for some types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-collections Area: std::collections. C-feature-request Category: A feature request, i.e: not implemented / a PR. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

8 participants