歪斜堆 C++ 代码


歪斜堆 C++ 代码

/******************************************************************************
* File: SkewBinomialHeap.hh
* Author: Keith Schwarz (htiek@cs.stanford.edu)
*
* An implementation of a priority queue backed by a skew binomial heap.  Like
* regular binomial heaps, skew binomial heaps are forests of specially-shaped
* trees based on the structure of the numeric representation of the heap size.
* Unlike regular binomial heaps, which are based on the binary numeral system,
* skew binomial heaps are based on the skew binary numeral system.  The skew
* binary numeral system has each digit take the value 2^{k+1} - 1, and each
* digit can be either 0, 1, or 2.  By convention, numbers are written in a way
* where the digit 2 must be the least-significant nonzero digit in the
* representation.  The first few numbers, written in this way, are 0, 1, 2,
* 10, 12, 20, 100, 101.  This may at first seem like a seemingly arbitrary
* pattern, but there actually is a deal of method to this madness.  To add one
* to a skew binary number, look at the last nonzero digit.  If it's a two,
* then we can use the fact that
*
*            2 * (2^{k+1} - 1) + 1 = 2^{k + 2} - 2 + 1 = 2^{k + 2}
*
* to remove the 2 digit and then increment the next digit by one.  To see this
* in action, consider the skew binary number 1011200 = 187 (dec).  We can add
* one to this number by removing the last 2, then incrementing the 1 that
* precedes it by one.  This yields 1012000 = 188(dec).  If, on the other hand,
* the last digit is not 2, then we can add one to the representation while
* enforcing our invariant by simply incrementing the 1s digit from 0 to 1 or
* from 1 to 2.  A quick inductive proof can show that this gives the proper
* mathematical quantity, as well as having all the invariants hold.
*
* The advantage of writing numbers in skew binary is that, given a suitable
* representation for the bits, any number can be incremented in worst-case
* constant time.  Suppose that we store the number as a list of the nonzero
* digits, together with tags on each indicating what position those digits
* are.  Then we can increment by one as follows:
*
* - If the last digit is a 2, then remove it.  If the next digit after the 2
*   is not in the next position, add a new digit with value 1 in the next
*   position.  Otherwise increment the digit in that position from 1 to 2.
* - Otherwise, if the last digit is a 1 in the 1s place, increment it to 2.
* - Otherwise, if the last digit is not in the 1s place, add a 1 in the 1s
*   place.
*
* This logic is a bit complex, but fortunately it can be implemented in time
* O(1).
*
* The idea behind the skew binomial heap is to store a family of heap-ordered
* trees of different "ranks" in the same way that numbers are written in skew
* binary.  There can be at most two trees of each rank, and if there are two
* trees of a given rank then that rank must be the lowest rank.  These trees
* are shaped in a way that allows them to be merged in one of two ways, either
* by a regular merge, which takes two trees of the same rank r and produces a
* new tree of rank r + 1, or by a "skew merge," which takes two trees of rank
* r and a singleton element, then produces a new tree of rank r + 1 holding
* both these trees and the new element.
*
* The structure of these trees, as developed by Chris Okasaki, is based on a
* more-or-less straightforward modification of binomial trees.  A skew
* binomial tree is defined recursively:
*
* - A skew binomial tree of order zero is a single node.
* - A skew binomial tree of order n + 1 is a skew binomial heap of order n
*   with a skew binomial tree of order n as a child, along with a list of
*   skew binomial trees of order 0 of length no greater than n + 1.
*
* Up to the part about the list of nodes, this description of a skew binomial
* tree is equivalent to a regular binomial tree.  The difference only shows up
* when we talk about skew merging.  To do a regular merge on skew binomial
* trees, we use the same logic for regular binomial tree merges - make the
* tree with the smaller root the root of the resulting tree, then add the tree
* with the larger root as a child.  In a skew merge, we do a regular merge
* first, then consider the new singleton element we're adding.  If the
* singleton is larger than the root of this new tree, we append it to the list
* of singleton nodes.  If it is smaller, we swap it with the root of the old
* tree, then add the root of the old tree as a singleton tree to the list of
* singleton nodes.
*
* This in part explains the limitation of why the list of singleton nodes
* can't be larger than size n + 1.  Each skew merge adds at most one new node
* to that list, and so after k skew merges the list can have at most k
* elements in it.
*
* Notice that because each skew binomial tree can have a list of singleton
* nodes with a variable number of nodes in it, the number of nodes in a skew
* binomial tree is not fixed.  However, it does fit in a definitive range.
* Consider any skew binomial tree of order r.  If none of the nodes in the
* tree have any nodes in their singleton list, then the tree has the same size
* and shape of a regular binomial tree, and thus has 2^r nodes.  If, on the
* other hand, every node has the maximum number of singleton nodes, we can
* prove by induction that the tree has 2^r - 1 additional nodes, for a total
* of 2^{r + 1} - 1 nodes.  The proof is as follows.  As a base case, a skew
* binomial tree of order 0 has no extra nodes, and 2^0 - 1 = 1 - 1 = 0.  For

* number of extra nodes in the tree (denoted E(r)) is 2^n' - 1.  Then we have
* that
*          n-1
* E(n) = ( sum  E(i) ) + n
*         i = 0
*
* This follows because the skew binomial tree of order n has children of order
* 0, 1, ..., n - 1, each of which have the maximum number of nodes, and itself
* carries n extra nodes.  Using the inductive hypothesis and rearranging, we
* get
*
*
*          n-1                 n-1
* E(n) = ( sum  E(i) ) + n = ( sum  2^i - 1) + n
*         i = 0               i = 0
*
*                              n-1
*                          = ( sum 2^i ) - n + n
*                             i = 0
*
*                              n-1
*                          = ( sum 2^i ) = 2^n - 1
*                             i = 0
*
* This last step follows from a well-known and easily verified result.
*
* The net result of this proof is that we can bound the number of nodes in a
* skew binomial tree of order n by the range [2^n, 2^{n + 1}).  This means
* that for any number k, if we partition the elements of k into a forest of
* skew binomial trees allowing for no more than two trees of each order, there
* are at most O(lg k) such trees, each of which has order at most O(lg k).
*
* Now that we know the shape of these trees and the way in which they are
* merged and skew-merged, we can start discussing basic operations of a skew
* binomial heap made of a forest of these trees.  The heap will be a forest of
* skew binomial trees with the following restrictions:
*
* - Each tree is heap-ordered.
* - There are at most two trees of any order.
* - If there are two trees of the same order, that is the lowest order of the
*   trees.
*
* Note the similarity between this definition and the way in which we write
* skew binary numbers.
*
* To find the minimum element of the skew binomial heap, we simply scan over
* all the heaps looking for the minimum element; this can be done in O(lg n)
* time, since there are at most O(lg n) trees.  To insert an element, we look
* at the two lowest-order trees in the heap.  If they have the same order, we
* skew merge them together into a tree of the next-highest order.  If not,
* then we add the new element as a tree of order 0.
*
* To merge two skew binomial heaps, we reduce the problem to one akin to a
* standard binomial heap merge.  First, we ensure that each heap has at most
* tree of each order by merging together any two trees with the same order,
* then doing a standard binomial heap "ripple-carry" operation to ensure that
* there is at most one tree of each order.  This takes time O(lg n), since
* there are at most O(lg n) trees.  Finally, we merge the two lists of trees
* together using the same logic as for binomial heaps by mimicking binary
* addition.  This also takes O(lg n) time, since there are at most O(lg n)
* trees.
*
* To dequeue the minimum element, we locate the minimum element as before and
* remove it to expose a forest of skew binomial trees, along with a list of
* singleton trees.  We then merge together the forest of trees with the top
* list of trees, which takes O(lg n) time since there are O(lg n) top-level
* trees and O(lg n) exposed trees, since if the tree whose min was removed had
* order r, there are r - 1 trees, and the maximum possible order is O(lg n).
* Finally, we call insert once for each singleton tree, and since there are at
* most O(lg n) of them and each insert takes O(1), this step completes in
* O(lg n) for a net runtime of O(lg n) for the dequeue step.
*/
#ifndef SkewBinomialHeap_Included
#define SkewBinomialHeap_Included





/**
* A priority queue implemented as a skew binomial heap.  The queue always
* provides access to the smallest element it contains.
*/

class SkewBinomialHeap {
public:
  /**
   * Constructor: SkewBinomialHeap();

   * --------------------------------------------------------------------------
   * Constructs a new, empty SkewBinomialHeap.
   */
  explicit SkewBinomialHeap(Comparator comp = Comparator());
  /**
   * Destructor: ~SkewBinomialHeap();
   * Usage: (implicit)
   * --------------------------------------------------------------------------
   * Deallocates the skew binomial heap, freeing all allocated memory.
   */
  ~SkewBinomialHeap();
  /**
   * Copy functions: SkewBinomialHeap(const SkewBinomialHeap& rhs);
   *                 SkewBinomialHeap& operator= (const SkewBinomialHeap& rhs);

   *                              clone = extant;
   * --------------------------------------------------------------------------
   * Deep-copies this skew binomial heap.
   */
  SkewBinomialHeap(const SkewBinomialHeap& rhs);
  SkewBinomialHeap& operator= (const SkewBinomialHeap& rhs);
  /**
   * void push(const T& toAdd);
   * Usage: sbh.add(137);
   * --------------------------------------------------------------------------
   * Adds a new element to the skew binomial heap.
   */
  void push(const T& toAdd);
  /**
   * void pop();
   * Usage: sbh.pop();
   * --------------------------------------------------------------------------
   * Removes, but does not return, the minimum element of the skew binomial
   * heap.  If the heap is empty, the behavior is undefined.
   */
  void pop();
  /**
   * const T& top() const;

   * --------------------------------------------------------------------------
   * Returns a reference to the smallest element in the heap.  This reference
   * is only guaranteed to be valid until the next call to push() or pop(),
   * or merge(), and becomes invalid if this object is merged with another.  If
   * the heap is empty, the behavior is undefined.
   */
  const T& top() const;
  /**
   * void merge(SkewBinomialTree& other);
   * Usage: lhs.merge(rhs);
   * --------------------------------------------------------------------------
   * Updates this object to contain all of its elements, plus all of the
   * elements in the other skew binomial heap.  The argument is destructively
   * modified to be empty upon exit.
   */
  void merge(SkewBinomialHeap& other);
  /**
   * size_t size() const;
   * Usage: size_t s = sbh.size();
   * --------------------------------------------------------------------------
   * Returns the number of elements in the skew binomial heap.
   */
  size_t size() const;
  /**
   * bool empty() const;
   * Usage: while (!sbh.empty()) { ... }
   * --------------------------------------------------------------------------
   * Returns whether this skew binomial heap is empty.
   */
  bool empty() const;
  /**
   * void swap(SkewBinomialHeap& other);
   * Usage: lhs.swap(rhs);
   * --------------------------------------------------------------------------
   * Exchanges the contents of this skew binomial heap with some other skew
   * binomial heap in O(1).
   */
  void swap(SkewBinomialHeap& other);
private:
  /* A utility class representing a skew binomial tree. */
  struct Tree {
    size_t            mOrder;      // The order of the tree.


    T                 mValue;      // The value stored in this node.
    /**
     * Utility constructor: Tree(const T& value, size_t order);
     * Usage: new Tree(value, 2);
     * ------------------------------------------------------------------------
     * Constructs a new skew binomial tree of the specified order and with the
     * indicated value.
     */
    Tree(const T& value, size_t order) : mOrder(order), mValue(value) {
      // Handled in initializer list.
    }
  };
  /* The trees in this heap. */

  /* A cached count of the number of elements here. */
  size_t mSize;
  /* The comparator to use here. */
  Comparator mComp;
  /* Utility function to free a single tree. */
  static void freeTree(Tree* toFree);
  /* Utility function that inserts a singleton tree into the tree.  The size is
   * not updated, but the underlying tree structure is.
   */
  void insertSingleton(Tree* singleton);
  /* Utility function that given two trees performs a standard merge on those
   * trees to produce a new tree.
   */
  Tree* mergeTrees(Tree* first, Tree* second);
  /* Utility function that, given two list of trees, merges those trees into
   * one sorted list.  More specifically, the list of trees of the first two
   * arguments are merged together.  The first list of trees holds the result,
   * while the second list of trees is emptied.
   */

  /* Utility function that finds and returns the index of the smallest element
   * in the list of trees.
   */
  size_t indexOfMin() const;
  /* Utility function that clones a skew binomial tree and returns the new
   * tree.
   */
  static Tree* cloneTree(const Tree* toClone);
};
/* * * * * Implementation Below This Point * * * * */
/* Constructor stores the indicated comparator. */


  : mSize(0), mComp(comp) {
  // Handled in initializer list.
}
/* Destructor walks over the trees, recursively deallocating them. */


  std::for_each(mTrees.begin(), mTrees.end(), &SkewBinomialHeap::freeTree);
}
/* Freeing a tree involves freeing its singletons and its children. */


  /* If the there is no tree, don't free anything. */
  if (!toFree) return;
  /* Free the tree's singletons. */
  std::for_each(toFree->mSingletons.begin(), toFree->mSingletons.end(),
                &SkewBinomialHeap::freeTree);
  /* Free the tree's children. */
  std::for_each(toFree->mChildren.begin(), toFree->mChildren.end(),
                &SkewBinomialHeap::freeTree);
  /* Free the node itself. */
  delete toFree;
}
/* Size queries return the cached size. */


  return mSize;
}
/* Emptiness queries return whether the size is zero. */


  return size() == 0;
}
/* Adding an element to the tree wraps it up in a singleton, then merges it
* into the tree.
*/


  /* Wrap the object up in a new singleton tree and add it. */
  insertSingleton(new Tree(value, 0));
  /* Increment our cached copy of the size so we track the number of elements
   * correctly.
   */
  ++mSize;
}
/* Inserting a singleton tree follows the rules specified in the file comments
* to combine the new singleton with the existing trees.
*/


  assert (toAdd && toAdd->mOrder == 0);
  /* If the last two trees have the same order, merge them together into one
   * tree using a skew merge.
   */
  if (mTrees.size() >= 2 && mTrees[0]->mOrder == mTrees[1]->mOrder) {
    /* First, remove both trees from the tree list; we're going to be replacing
     * them with one new tree.
     */
    Tree* first  = mTrees.front(); mTrees.pop_front();
    Tree* second = mTrees.front(); mTrees.pop_front();
    /* Link the trees using a standard merge. */
    Tree* newTree = mergeTrees(first, second);
    /* Now, if the singleton tree has a value less than that of the new union,
     * swap its value with the value atop the tree.
     */
    if (mComp(toAdd->mValue, newTree->mValue))
      std::swap(toAdd->mValue, newTree->mValue);
    /* Make the singleton (which might now be holding a value other than the
     * one it started with) an entry in the resulting singleton list.
     */
    newTree->mSingletons.push_back(toAdd);
    /* Finally, put this tree at the front of the list of trees. */
    mTrees.push_front(newTree);
  }
  /* Otherwise, just prepend the tree to the front of the list; we have nothing
   * to merge.
   */
  else {
    mTrees.push_front(toAdd);
  }
}
/* Merging two trees makes the tree with the smaller root the parent of the
* other tree, then increases its order.
*/



  assert (one && two);
  /* Make sure that the first tree has a lower value, and exchange the two if
   * that isn't the case so we can pretend it was all along.
   */
  if (mComp(two->mValue, one->mValue))
    std::swap(one, two);
  /* Make two the last child of one. */
  one->mChildren.push_back(two);
  /* Increase the order of the first tree, since it now has another child. */
  ++one->mOrder;
  /* Hand back this tree as the new root of the merger. */
  return one;
}
/* Locating the minimum element works by scanning the trees for the lowest
* value and handing back a reference to it.
*/


  assert (!empty());
  return mTrees[indexOfMin()]->mValue;
}
/* To find the minimum element, we scan the roots of all the trees. */


  /* Guess that the smallest element is in the first tree. */
  int min = 0;
  /* Update this guess. */

    if (mComp(mTrees[i]->mValue, mTrees[min]->mValue))
      min = i;
  /* Hand back a reference to the value. */
  return min;
}
/* Merge operation forwards the to a helper function which does the
* actual logic.
*/


  /* Do the actual logic necessary to merge the heaps together. */
  mergeHeaps(mTrees, other.mTrees);
  /* Update the size information of both trees; this heap just grew in size,
   * while the other heap lost all its elements.
   */
  mSize += other.mSize;
  other.mSize = 0;
}
/* Given two lists of trees, merges them together into a single list of trees
* holding all the original elements.
*/



  /* Merge all of the trees from the two input sequences together by their
   * length.  We'll use this to simplify the processing logic.
   */

  while (!lhs.empty() && !rhs.empty()) {

      allTrees.push_back(lhs.front()); lhs.pop_front();
    } else {
      allTrees.push_back(rhs.front()); rhs.pop_front();
    }
  }
  /* Add the remaining heaps to the list as well.  Only one of these two
   * branches will execute, so this maintains the sorted order.
   */
  allTrees.insert(allTrees.end(), lhs.begin(), lhs.end());
  allTrees.insert(allTrees.end(), rhs.begin(), rhs.end());
  lhs.clear();  rhs.clear();
  /* The merge process is similar to binary addition.  We iterate across the
   * trees, at each point looking at all the trees of a certain order.  If
   * there's exactly one tree of each size, then we just put that in the output
   * list.  If there are two, we put nothing, then store the merge of the tree
   * as a "carry."  Finally, if there are three, we store one, then use the
   * merge of the other two as a carry.
   *
   * The difference from regular binary addition that we have to consider is
   * that in a skew binomial heap there might be multiple trees of the same
   * order.  In the worst case, we'll have four different trees of the same
   * order at any one time (since there's at most two trees of the same order
   * in each tree).  Consequently, we'll generalize the logic a bit.  For each
   * two trees of a given order that we find, we'll merge them together into
   * a tree of the next highest-order.  Any odd tree remaining is left in the
   * output sequence.
   */
  while (!allTrees.empty()) {
    /* Populate a buffer with trees that have the same order as the first tree
     * left in the worklist.  Initially this just has the first tree.
     */

    ofSameOrder.push_back(allTrees.front()); allTrees.pop_front();
    /* Move all other trees into this list with the same order. */
    while (!allTrees.empty() &&
           allTrees.front()->mOrder == ofSameOrder.front()->mOrder) {
      ofSameOrder.push_back(allTrees.front()); allTrees.pop_front();
    }
    /* If we have an odd number of trees, write one of them to the output. */
    if (ofSameOrder.size() % 2 == 1) {
      lhs.push_back(ofSameOrder.front()); ofSameOrder.pop_front();
    }
    assert (ofSameOrder.size() % 2 == 0);
    /* Keep merging pairs of remaining trees and putting them back in the
     * worklist.
     */

      allTrees.push_front(mergeTrees(ofSameOrder[i], ofSameOrder[i + 1]));
  }
}
/* Removing the min element requires removing it from the list of trees, then
* adding the child trees and singletons back in.
*/


  assert (!empty());
  /* Track down the index of the smallest tree, then remove it from the
   * list of trees.
   */
  const size_t minIndex = indexOfMin();
  Tree* toRemove = mTrees[minIndex];
  mTrees.erase(mTrees.begin() + minIndex);
  /* Now, we need to merge all of the child trees back in to the master
   * list.
   */
  mergeHeaps(mTrees, toRemove->mChildren);
  /* For each of the singletons held by this tree, insert that singleton back
   * into the tree.
   */

    insertSingleton(toRemove->mSingletons[i]);
  /* Decrement the total number of trees remaining here; we're about to remove
   * something.
   */
  --mSize;
  /* Free the memory associated with this entry. */
  delete toRemove;
}
/* Cloning a tree recursively copies all of the subtrees. */



  /* The clone of the empty tree is the empty tree. */
  if (!toClone) return NULL;
  /* Otherwise, clone the tree's value and order. */
  Tree* result = new Tree(toClone->mValue, toClone->mOrder);
  /* Deep-copy the children and singletons. */

    result->mChildren.push_back(cloneTree(toClone->mChildren[i]));

    result->mSingletons.push_back(cloneTree(toClone->mSingletons[i]));
  /* Hand back the copied tree. */
  return result;
}
/* To deep-copy a skew binomial heap, we copy each tree and the cached value
* of the number of nodes present.
*/


  /* Clone all the trees in order. */

    mTrees.push_back(cloneTree(rhs.mTrees[i]));
  /* Copy the size over so we know how many elements are here. */
  mSize = rhs.mSize;
  mComp = rhs.mComp;
}
/* To exchange two skew binomial heaps, we just swap the trees and size. */


  std::swap(mSize, rhs.mSize);
  std::swap(mComp, rhs.mComp);
  mTrees.swap(rhs.mTrees);
}
/* Assignment implemented with copy-and-swap. */



  SkewBinomialHeap copy = rhs;
  swap(copy);
  return *this;
}
#endif

声明: 除非转自他站(如有侵权,请联系处理)外,本文采用 BY-NC-SA 协议进行授权 | 智乐兔
转载请注明:转自《歪斜堆 C++ 代码
本文地址:https://www.zhiletu.com/archives-7841.html
关注公众号:智乐兔

赞赏

wechat pay微信赞赏alipay pay支付宝赞赏

上一篇
下一篇

相关文章

在线留言

你必须 登录后 才能留言!

在线客服
在线客服 X

售前: 点击这里给我发消息
售后: 点击这里给我发消息

智乐兔官微