auto merge of #20133 : apasel422/rust/binary_heap, r=alexcrichton

Some more tidying up.
This commit is contained in:
bors 2014-12-26 21:51:48 +00:00
commit c06edbad34

View File

@ -11,15 +11,15 @@
//! A priority queue implemented with a binary heap. //! A priority queue implemented with a binary heap.
//! //!
//! Insertion and popping the largest element have `O(log n)` time complexity. Checking the largest //! Insertion and popping the largest element have `O(log n)` time complexity. Checking the largest
//! element is `O(1)`. Converting a vector to a priority queue can be done in-place, and has `O(n)` //! element is `O(1)`. Converting a vector to a binary heap can be done in-place, and has `O(n)`
//! complexity. A priority queue can also be converted to a sorted vector in-place, allowing it to //! complexity. A binary heap can also be converted to a sorted vector in-place, allowing it to
//! be used for an `O(n log n)` in-place heapsort. //! be used for an `O(n log n)` in-place heapsort.
//! //!
//! # Examples //! # Examples
//! //!
//! This is a larger example which implements [Dijkstra's algorithm][dijkstra] //! This is a larger example that implements [Dijkstra's algorithm][dijkstra]
//! to solve the [shortest path problem][sssp] on a [directed graph][dir_graph]. //! to solve the [shortest path problem][sssp] on a [directed graph][dir_graph].
//! It showcases how to use the `BinaryHeap` with custom types. //! It shows how to use `BinaryHeap` with custom types.
//! //!
//! [dijkstra]: http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm //! [dijkstra]: http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
//! [sssp]: http://en.wikipedia.org/wiki/Shortest_path_problem //! [sssp]: http://en.wikipedia.org/wiki/Shortest_path_problem
@ -32,7 +32,7 @@
//! #[deriving(Copy, Eq, PartialEq)] //! #[deriving(Copy, Eq, PartialEq)]
//! struct State { //! struct State {
//! cost: uint, //! cost: uint,
//! position: uint //! position: uint,
//! } //! }
//! //!
//! // The priority queue depends on `Ord`. //! // The priority queue depends on `Ord`.
@ -55,13 +55,13 @@
//! // Each node is represented as an `uint`, for a shorter implementation. //! // Each node is represented as an `uint`, for a shorter implementation.
//! struct Edge { //! struct Edge {
//! node: uint, //! node: uint,
//! cost: uint //! cost: uint,
//! } //! }
//! //!
//! // Dijkstra's shortest path algorithm. //! // Dijkstra's shortest path algorithm.
//! //!
//! // Start at `start` and use `dist` to track the current shortest distance //! // Start at `start` and use `dist` to track the current shortest distance
//! // to each node. This implementation isn't memory efficient as it may leave duplicate //! // to each node. This implementation isn't memory-efficient as it may leave duplicate
//! // nodes in the queue. It also uses `uint::MAX` as a sentinel value, //! // nodes in the queue. It also uses `uint::MAX` as a sentinel value,
//! // for a simpler implementation. //! // for a simpler implementation.
//! fn shortest_path(adj_list: &Vec<Vec<Edge>>, start: uint, goal: uint) -> uint { //! fn shortest_path(adj_list: &Vec<Vec<Edge>>, start: uint, goal: uint) -> uint {
@ -71,21 +71,16 @@
//! let mut heap = BinaryHeap::new(); //! let mut heap = BinaryHeap::new();
//! //!
//! // We're at `start`, with a zero cost //! // We're at `start`, with a zero cost
//! dist[start] = 0u; //! dist[start] = 0;
//! heap.push(State { cost: 0u, position: start }); //! heap.push(State { cost: 0, position: start });
//! //!
//! // Examine the frontier with lower cost nodes first (min-heap) //! // Examine the frontier with lower cost nodes first (min-heap)
//! loop { //! while let Some(State { cost, position }) = heap.pop() {
//! let State { cost, position } = match heap.pop() {
//! None => break, // empty
//! Some(s) => s
//! };
//!
//! // Alternatively we could have continued to find all shortest paths //! // Alternatively we could have continued to find all shortest paths
//! if position == goal { return cost } //! if position == goal { return cost; }
//! //!
//! // Important as we may have already found a better way //! // Important as we may have already found a better way
//! if cost > dist[position] { continue } //! if cost > dist[position] { continue; }
//! //!
//! // For each node we can reach, see if we can find a way with //! // For each node we can reach, see if we can find a way with
//! // a lower cost going through this node //! // a lower cost going through this node
@ -108,7 +103,7 @@
//! fn main() { //! fn main() {
//! // This is the directed graph we're going to use. //! // This is the directed graph we're going to use.
//! // The node numbers correspond to the different states, //! // The node numbers correspond to the different states,
//! // and the edge weights symbolises the cost of moving //! // and the edge weights symbolize the cost of moving
//! // from one node to another. //! // from one node to another.
//! // Note that the edges are one-way. //! // Note that the edges are one-way.
//! // //! //
@ -126,7 +121,7 @@
//! // //! //
//! // The graph is represented as an adjacency list where each index, //! // The graph is represented as an adjacency list where each index,
//! // corresponding to a node value, has a list of outgoing edges. //! // corresponding to a node value, has a list of outgoing edges.
//! // Chosen for it's efficiency. //! // Chosen for its efficiency.
//! let graph = vec![ //! let graph = vec![
//! // Node 0 //! // Node 0
//! vec![Edge { node: 2, cost: 10 }, //! vec![Edge { node: 2, cost: 10 },
@ -184,10 +179,11 @@ impl<T: Ord> BinaryHeap<T> {
/// ///
/// ``` /// ```
/// use std::collections::BinaryHeap; /// use std::collections::BinaryHeap;
/// let heap: BinaryHeap<uint> = BinaryHeap::new(); /// let mut heap = BinaryHeap::new();
/// heap.push(4u);
/// ``` /// ```
#[unstable = "matches collection reform specification, waiting for dust to settle"] #[unstable = "matches collection reform specification, waiting for dust to settle"]
pub fn new() -> BinaryHeap<T> { BinaryHeap{data: vec!(),} } pub fn new() -> BinaryHeap<T> { BinaryHeap { data: vec![] } }
/// Creates an empty `BinaryHeap` with a specific capacity. /// Creates an empty `BinaryHeap` with a specific capacity.
/// This preallocates enough memory for `capacity` elements, /// This preallocates enough memory for `capacity` elements,
@ -198,7 +194,8 @@ impl<T: Ord> BinaryHeap<T> {
/// ///
/// ``` /// ```
/// use std::collections::BinaryHeap; /// use std::collections::BinaryHeap;
/// let heap: BinaryHeap<uint> = BinaryHeap::with_capacity(10u); /// let mut heap = BinaryHeap::with_capacity(10);
/// heap.push(4u);
/// ``` /// ```
#[unstable = "matches collection reform specification, waiting for dust to settle"] #[unstable = "matches collection reform specification, waiting for dust to settle"]
pub fn with_capacity(capacity: uint) -> BinaryHeap<T> { pub fn with_capacity(capacity: uint) -> BinaryHeap<T> {
@ -214,17 +211,17 @@ impl<T: Ord> BinaryHeap<T> {
/// use std::collections::BinaryHeap; /// use std::collections::BinaryHeap;
/// let heap = BinaryHeap::from_vec(vec![9i, 1, 2, 7, 3, 2]); /// let heap = BinaryHeap::from_vec(vec![9i, 1, 2, 7, 3, 2]);
/// ``` /// ```
pub fn from_vec(xs: Vec<T>) -> BinaryHeap<T> { pub fn from_vec(vec: Vec<T>) -> BinaryHeap<T> {
let mut q = BinaryHeap{data: xs,}; let mut heap = BinaryHeap { data: vec };
let mut n = q.len() / 2; let mut n = heap.len() / 2;
while n > 0 { while n > 0 {
n -= 1; n -= 1;
q.siftdown(n) heap.sift_down(n);
} }
q heap
} }
/// An iterator visiting all values in underlying vector, in /// Returns an iterator visiting all values in the underlying vector, in
/// arbitrary order. /// arbitrary order.
/// ///
/// # Examples /// # Examples
@ -251,10 +248,10 @@ impl<T: Ord> BinaryHeap<T> {
/// ///
/// ``` /// ```
/// use std::collections::BinaryHeap; /// use std::collections::BinaryHeap;
/// let pq = BinaryHeap::from_vec(vec![1i, 2, 3, 4]); /// let heap = BinaryHeap::from_vec(vec![1i, 2, 3, 4]);
/// ///
/// // Print 1, 2, 3, 4 in arbitrary order /// // Print 1, 2, 3, 4 in arbitrary order
/// for x in pq.into_iter() { /// for x in heap.into_iter() {
/// // x has type int, not &int /// // x has type int, not &int
/// println!("{}", x); /// println!("{}", x);
/// } /// }
@ -264,20 +261,19 @@ impl<T: Ord> BinaryHeap<T> {
IntoIter { iter: self.data.into_iter() } IntoIter { iter: self.data.into_iter() }
} }
/// Returns the greatest item in a queue, or `None` if it is empty. /// Returns the greatest item in the binary heap, or `None` if it is empty.
/// ///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use std::collections::BinaryHeap; /// use std::collections::BinaryHeap;
///
/// let mut heap = BinaryHeap::new(); /// let mut heap = BinaryHeap::new();
/// assert_eq!(heap.peek(), None); /// assert_eq!(heap.peek(), None);
/// ///
/// heap.push(1i); /// heap.push(1i);
/// heap.push(5i); /// heap.push(5);
/// heap.push(2i); /// heap.push(2);
/// assert_eq!(heap.peek(), Some(&5i)); /// assert_eq!(heap.peek(), Some(&5));
/// ///
/// ``` /// ```
#[stable] #[stable]
@ -285,15 +281,15 @@ impl<T: Ord> BinaryHeap<T> {
self.data.get(0) self.data.get(0)
} }
/// Returns the number of elements the queue can hold without reallocating. /// Returns the number of elements the binary heap can hold without reallocating.
/// ///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use std::collections::BinaryHeap; /// use std::collections::BinaryHeap;
/// /// let mut heap = BinaryHeap::with_capacity(100);
/// let heap: BinaryHeap<uint> = BinaryHeap::with_capacity(100u); /// assert!(heap.capacity() >= 100);
/// assert!(heap.capacity() >= 100u); /// heap.push(4u);
/// ``` /// ```
#[unstable = "matches collection reform specification, waiting for dust to settle"] #[unstable = "matches collection reform specification, waiting for dust to settle"]
pub fn capacity(&self) -> uint { self.data.capacity() } pub fn capacity(&self) -> uint { self.data.capacity() }
@ -313,13 +309,15 @@ impl<T: Ord> BinaryHeap<T> {
/// ///
/// ``` /// ```
/// use std::collections::BinaryHeap; /// use std::collections::BinaryHeap;
/// /// let mut heap = BinaryHeap::new();
/// let mut heap: BinaryHeap<uint> = BinaryHeap::new(); /// heap.reserve_exact(100);
/// heap.reserve_exact(100u); /// assert!(heap.capacity() >= 100);
/// assert!(heap.capacity() >= 100u); /// heap.push(4u);
/// ``` /// ```
#[unstable = "matches collection reform specification, waiting for dust to settle"] #[unstable = "matches collection reform specification, waiting for dust to settle"]
pub fn reserve_exact(&mut self, additional: uint) { self.data.reserve_exact(additional) } pub fn reserve_exact(&mut self, additional: uint) {
self.data.reserve_exact(additional);
}
/// Reserves capacity for at least `additional` more elements to be inserted in the /// Reserves capacity for at least `additional` more elements to be inserted in the
/// `BinaryHeap`. The collection may reserve more space to avoid frequent reallocations. /// `BinaryHeap`. The collection may reserve more space to avoid frequent reallocations.
@ -332,88 +330,82 @@ impl<T: Ord> BinaryHeap<T> {
/// ///
/// ``` /// ```
/// use std::collections::BinaryHeap; /// use std::collections::BinaryHeap;
/// /// let mut heap = BinaryHeap::new();
/// let mut heap: BinaryHeap<uint> = BinaryHeap::new(); /// heap.reserve(100);
/// heap.reserve(100u); /// assert!(heap.capacity() >= 100);
/// assert!(heap.capacity() >= 100u); /// heap.push(4u);
/// ``` /// ```
#[unstable = "matches collection reform specification, waiting for dust to settle"] #[unstable = "matches collection reform specification, waiting for dust to settle"]
pub fn reserve(&mut self, additional: uint) { pub fn reserve(&mut self, additional: uint) {
self.data.reserve(additional) self.data.reserve(additional);
} }
/// Discards as much additional capacity as possible. /// Discards as much additional capacity as possible.
#[unstable = "matches collection reform specification, waiting for dust to settle"] #[unstable = "matches collection reform specification, waiting for dust to settle"]
pub fn shrink_to_fit(&mut self) { pub fn shrink_to_fit(&mut self) {
self.data.shrink_to_fit() self.data.shrink_to_fit();
} }
/// Removes the greatest item from a queue and returns it, or `None` if it /// Removes the greatest item from the binary heap and returns it, or `None` if it
/// is empty. /// is empty.
/// ///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use std::collections::BinaryHeap; /// use std::collections::BinaryHeap;
///
/// let mut heap = BinaryHeap::from_vec(vec![1i, 3]); /// let mut heap = BinaryHeap::from_vec(vec![1i, 3]);
/// ///
/// assert_eq!(heap.pop(), Some(3i)); /// assert_eq!(heap.pop(), Some(3));
/// assert_eq!(heap.pop(), Some(1i)); /// assert_eq!(heap.pop(), Some(1));
/// assert_eq!(heap.pop(), None); /// assert_eq!(heap.pop(), None);
/// ``` /// ```
#[unstable = "matches collection reform specification, waiting for dust to settle"] #[unstable = "matches collection reform specification, waiting for dust to settle"]
pub fn pop(&mut self) -> Option<T> { pub fn pop(&mut self) -> Option<T> {
match self.data.pop() { self.data.pop().map(|mut item| {
None => { None }
Some(mut item) => {
if !self.is_empty() { if !self.is_empty() {
swap(&mut item, &mut self.data[0]); swap(&mut item, &mut self.data[0]);
self.siftdown(0); self.sift_down(0);
}
Some(item)
}
} }
item
})
} }
/// Pushes an item onto the queue. /// Pushes an item onto the binary heap.
/// ///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use std::collections::BinaryHeap; /// use std::collections::BinaryHeap;
///
/// let mut heap = BinaryHeap::new(); /// let mut heap = BinaryHeap::new();
/// heap.push(3i); /// heap.push(3i);
/// heap.push(5i); /// heap.push(5);
/// heap.push(1i); /// heap.push(1);
/// ///
/// assert_eq!(heap.len(), 3); /// assert_eq!(heap.len(), 3);
/// assert_eq!(heap.peek(), Some(&5i)); /// assert_eq!(heap.peek(), Some(&5));
/// ``` /// ```
#[unstable = "matches collection reform specification, waiting for dust to settle"] #[unstable = "matches collection reform specification, waiting for dust to settle"]
pub fn push(&mut self, item: T) { pub fn push(&mut self, item: T) {
let old_len = self.len(); let old_len = self.len();
self.data.push(item); self.data.push(item);
self.siftup(0, old_len); self.sift_up(0, old_len);
} }
/// Pushes an item onto a queue then pops the greatest item off the queue in /// Pushes an item onto the binary heap, then pops the greatest item off the queue in
/// an optimized fashion. /// an optimized fashion.
/// ///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use std::collections::BinaryHeap; /// use std::collections::BinaryHeap;
///
/// let mut heap = BinaryHeap::new(); /// let mut heap = BinaryHeap::new();
/// heap.push(1i); /// heap.push(1i);
/// heap.push(5i); /// heap.push(5);
/// ///
/// assert_eq!(heap.push_pop(3i), 5); /// assert_eq!(heap.push_pop(3), 5);
/// assert_eq!(heap.push_pop(9i), 9); /// assert_eq!(heap.push_pop(9), 9);
/// assert_eq!(heap.len(), 2); /// assert_eq!(heap.len(), 2);
/// assert_eq!(heap.peek(), Some(&3i)); /// assert_eq!(heap.peek(), Some(&3));
/// ``` /// ```
pub fn push_pop(&mut self, mut item: T) -> T { pub fn push_pop(&mut self, mut item: T) -> T {
match self.data.get_mut(0) { match self.data.get_mut(0) {
@ -425,30 +417,29 @@ impl<T: Ord> BinaryHeap<T> {
}, },
} }
self.siftdown(0); self.sift_down(0);
item item
} }
/// Pops the greatest item off a queue then pushes an item onto the queue in /// Pops the greatest item off the binary heap, then pushes an item onto the queue in
/// an optimized fashion. The push is done regardless of whether the queue /// an optimized fashion. The push is done regardless of whether the binary heap
/// was empty. /// was empty.
/// ///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use std::collections::BinaryHeap; /// use std::collections::BinaryHeap;
///
/// let mut heap = BinaryHeap::new(); /// let mut heap = BinaryHeap::new();
/// ///
/// assert_eq!(heap.replace(1i), None); /// assert_eq!(heap.replace(1i), None);
/// assert_eq!(heap.replace(3i), Some(1i)); /// assert_eq!(heap.replace(3), Some(1));
/// assert_eq!(heap.len(), 1); /// assert_eq!(heap.len(), 1);
/// assert_eq!(heap.peek(), Some(&3i)); /// assert_eq!(heap.peek(), Some(&3));
/// ``` /// ```
pub fn replace(&mut self, mut item: T) -> Option<T> { pub fn replace(&mut self, mut item: T) -> Option<T> {
if !self.is_empty() { if !self.is_empty() {
swap(&mut item, &mut self.data[0]); swap(&mut item, &mut self.data[0]);
self.siftdown(0); self.sift_down(0);
Some(item) Some(item)
} else { } else {
self.push(item); self.push(item);
@ -463,7 +454,6 @@ impl<T: Ord> BinaryHeap<T> {
/// ///
/// ``` /// ```
/// use std::collections::BinaryHeap; /// use std::collections::BinaryHeap;
///
/// let heap = BinaryHeap::from_vec(vec![1i, 2, 3, 4, 5, 6, 7]); /// let heap = BinaryHeap::from_vec(vec![1i, 2, 3, 4, 5, 6, 7]);
/// let vec = heap.into_vec(); /// let vec = heap.into_vec();
/// ///
@ -494,35 +484,34 @@ impl<T: Ord> BinaryHeap<T> {
while end > 1 { while end > 1 {
end -= 1; end -= 1;
self.data.swap(0, end); self.data.swap(0, end);
self.siftdown_range(0, end) self.sift_down_range(0, end);
} }
self.into_vec() self.into_vec()
} }
// The implementations of siftup and siftdown use unsafe blocks in // The implementations of sift_up and sift_down use unsafe blocks in
// order to move an element out of the vector (leaving behind a // order to move an element out of the vector (leaving behind a
// zeroed element), shift along the others and move it back into the // zeroed element), shift along the others and move it back into the
// vector over the junk element. This reduces the constant factor // vector over the junk element. This reduces the constant factor
// compared to using swaps, which involves twice as many moves. // compared to using swaps, which involves twice as many moves.
fn siftup(&mut self, start: uint, mut pos: uint) { fn sift_up(&mut self, start: uint, mut pos: uint) {
unsafe { unsafe {
let new = replace(&mut self.data[pos], zeroed()); let new = replace(&mut self.data[pos], zeroed());
while pos > start { while pos > start {
let parent = (pos - 1) >> 1; let parent = (pos - 1) >> 1;
if new > self.data[parent] {
if new <= self.data[parent] { break; }
let x = replace(&mut self.data[parent], zeroed()); let x = replace(&mut self.data[parent], zeroed());
ptr::write(&mut self.data[pos], x); ptr::write(&mut self.data[pos], x);
pos = parent; pos = parent;
continue
}
break
} }
ptr::write(&mut self.data[pos], new); ptr::write(&mut self.data[pos], new);
} }
} }
fn siftdown_range(&mut self, mut pos: uint, end: uint) { fn sift_down_range(&mut self, mut pos: uint, end: uint) {
unsafe { unsafe {
let start = pos; let start = pos;
let new = replace(&mut self.data[pos], zeroed()); let new = replace(&mut self.data[pos], zeroed());
@ -540,33 +529,31 @@ impl<T: Ord> BinaryHeap<T> {
} }
ptr::write(&mut self.data[pos], new); ptr::write(&mut self.data[pos], new);
self.siftup(start, pos); self.sift_up(start, pos);
} }
} }
fn siftdown(&mut self, pos: uint) { fn sift_down(&mut self, pos: uint) {
let len = self.len(); let len = self.len();
self.siftdown_range(pos, len); self.sift_down_range(pos, len);
} }
/// Returns the length of the queue. /// Returns the length of the binary heap.
#[unstable = "matches collection reform specification, waiting for dust to settle"] #[unstable = "matches collection reform specification, waiting for dust to settle"]
pub fn len(&self) -> uint { self.data.len() } pub fn len(&self) -> uint { self.data.len() }
/// Returns true if the queue contains no elements /// Checks if the binary heap is empty.
#[unstable = "matches collection reform specification, waiting for dust to settle"] #[unstable = "matches collection reform specification, waiting for dust to settle"]
pub fn is_empty(&self) -> bool { self.len() == 0 } pub fn is_empty(&self) -> bool { self.len() == 0 }
/// Clears the queue, returning an iterator over the removed elements. /// Clears the binary heap, returning an iterator over the removed elements.
#[inline] #[inline]
#[unstable = "matches collection reform specification, waiting for dust to settle"] #[unstable = "matches collection reform specification, waiting for dust to settle"]
pub fn drain<'a>(&'a mut self) -> Drain<'a, T> { pub fn drain(&mut self) -> Drain<T> {
Drain { Drain { iter: self.data.drain() }
iter: self.data.drain(),
}
} }
/// Drops all items from the queue. /// Drops all items from the binary heap.
#[unstable = "matches collection reform specification, waiting for dust to settle"] #[unstable = "matches collection reform specification, waiting for dust to settle"]
pub fn clear(&mut self) { self.drain(); } pub fn clear(&mut self) { self.drain(); }
} }