Rank-Pairing Heap

概要

Rank-Pairing Heap は Fibonacci Heapと同じ性能を持ち、Pairing Heapのようなシンプルさを備えるheapである。

heap は half-ordered half tree という木構造で表現し、各ノードはrankを持ち、tree内で Rank Rule を満たすようにrankを保持する。

Rank-Pairing Heapではこのtreeをリストとして持ち、decrease-key操作等で分離したtreeをリストに追加、delete-min操作を行う際にtree同士をマージする。

計算量

  • find-min: \(O(1)\)

  • delete-min: ならし \(O(\log N)\)

  • insert: \(O(1)\)

  • decrease-key: ならし \(O(1)\)

実装

#include<vector>
#include<unordered_map>

using namespace std;

template<typename K, typename V>
class RankPairingHeap {
  struct Node {
    Node *left, *right, *parent;
    K key;
    V value;
    int rank;

    void init(K key, V value) const {
      this->key = key; this->value = value;
      this->left = this->right = this->parent = nullptr;
      this->rank = 0;
    }

    int left_rank() const {
      return left != nullptr ? left->rank : -1;
    }
    int right_rank() const {
      return right != nullptr ? right->rank : -1;
    }
  };

  Node *nds;
  int max_sz;
  vector<Node*> free_nodes, tmp;
  unordered_map<int, Node*> bucket;

  vector<Node*> roots;
  Node *min_root;
  unordered_map<V, Node*> nd_map;

  Node* new_node(K key, V value) {
    // assert(free_nodes.size() > 0);
    Node *nd = free_nodes.back(); free_nodes.pop_back();
    nd->init(key, value);
    nd_map[value] = nd;
    return nd;
  }

  void remove_node(Node *node) {
    nd_map.erase(node->value);
    free_nodes.push_back(node);
  }

  static Node* link(Node *nd_a, Node *nd_b) {
    if(nd_a->key > nd_b->key) {
      swap(nd_a, nd_b);
    }
    Node *left = nd_a->left;
    nd_a->left = nd_b; nd_b->parent = nd_a;
    nd_b->right = left;
    if(left != nullptr) {
      left->parent = nd_b;
    }
    nd_a->rank = nd_b->rank + 1;
    return nd_a;
  }

  void init() {
    min_root = nullptr;
    roots.clear(); free_nodes.clear();
    for(int i=0; i<max_sz; ++i) free_nodes.push_back(nds+i);
  }

public:
  struct Result {
    K key;
    V value;
  };

  RankPairingHeap(int max_sz) {
    this->max_sz = max_sz;
    nds = new Node[max_sz];
    free_nodes.reserve(max_sz);
    init();
  }

  ~RankPairingHeap() {
    delete[] nds;
  }

  void clear() {
    init();
  }

  bool empty() const {
    return (min_root == nullptr);
  }

  // insert: O(1)
  void push(V value, K key) {
    // assert(nd_map.count(value) == 0);
    Node *nd = new_node(key, value);
    if(min_root == nullptr) {
      min_root = nd;
    } else if(nd->key < min_root->key) {
      roots.push_back(min_root);
      min_root = nd;
    } else {
      roots.push_back(nd);
    }
  }

  // find-min: O(1)
  Result top() {
    // assert(!empty());
    return Result{min_root->key, min_root->value};
  }

  // delete-min: O(\log N) (amortized time)
  Result pop() {
    // assert(!empty());

    Node *target = min_root;
    Result result = {target->key, target->value};

    tmp.clear();
    bucket.clear();
    for(Node *nd : roots) {
      int rank = nd->rank;
      if(bucket.count(rank)) {
        Node *root = bucket[rank]; bucket.erase(rank);
        Node *new_root = link(nd, root);
        tmp.push_back(new_root);
      } else {
        bucket[rank] = nd;
      }
    }

    Node *cur = min_root->left;
    while(cur != nullptr) {
      Node *nxt = cur->right;
      cur->right = cur->parent = nullptr;
      cur->rank = cur->left_rank() + 1;

      int rank = cur->rank;
      if(bucket.count(rank)) {
        Node *root = bucket[rank]; bucket.erase(rank);
        Node *new_root = link(cur, root);
        tmp.push_back(new_root);
      } else {
        bucket[rank] = cur;
      }
      cur = nxt;
    }
    for(pair<int, Node*> p : bucket) {
      tmp.push_back(p.second);
    }

    roots.clear();
    if(tmp.size() > 0) {
      int k = 0, min_key = tmp[0]->key;
      for(int i=1; i<tmp.size(); ++i) {
        if(tmp[i]->key < min_key) {
          min_key = tmp[i]->key;
          k = i;
        }
      }
      for(int i=0; i<tmp.size(); ++i) {
        if(i == k) continue;
        roots.push_back(tmp[i]);
      }
      min_root = tmp[k];
    } else {
      min_root = nullptr;
    }
    remove_node(target);
    return result;
  }

  // decrease-key: O(1) (amortized time)
  void decrease_key(V value, K delta) {
    Node *target = nd_map[value];
    target->key -= delta;
    if(target->parent == nullptr) {
      if(target->key < min_root->key) {
        swap(nd_map[target->value], nd_map[min_root->value]);
        swap(target->key, min_root->key);
        swap(target->value, min_root->value);
      }
      return;
    }

    Node *child = target->right;
    Node *u = target->parent;
    target->right = target->parent = nullptr;
    if(u->left == target) {
      u->left = child;
    } else {
      u->right = child;
    }
    if(child != nullptr) {
      child->parent = u;
    }

    while(true) {
      if(u->parent == nullptr) {
        u->rank = u->left_rank() + 1;
        break;
      }
      int lr = u->left_rank(), rr = u->right_rank();
      int k = (lr == rr ? lr + 1 : max(lr, rr));
      if(u->rank == k) break;

      u->rank = k;
      u = u->parent;
    }

    target->rank = target->left_rank() + 1;
    if(target->key < min_root->key) {
      roots.push_back(min_root);
      min_root = target;
    } else {
      roots.push_back(target);
    }
  }

  void update(V value, K new_key) {
    if(nd_map.count(value) > 0) {
      Node *node = nd_map[value];
      K delta = node->key - new_key;
      // assert(delta >= 0);
      decrease_key(value, delta);
      return;
    }
    push(value, new_key);
  }
};

Verified

  • AOJ: "GRL_1_A: Single Source Shortest Path": source (C++14, 0.29sec)

参考

  • Haeupler, Bernhard, Siddhartha Sen, and Robert E. Tarjan. "Rank-pairing heaps." SIAM Journal on Computing 40.6 (2011): 1463-1485.