プライオリティーキューとは挿入した要素の優先順位が高いものから順に取り出せるという
若干便利なキューですが、要素を突っ込んだら最後。もう削除したり優先順位を変更できないことが普通です
(std::priority_queueなど)
そこでとある本で読んだIndexedPriorityQueueというのを実装してみました。
アクセサという若干苦し紛れのクラスを用いることで優先順位を後から変更したり要素を削除できるようにしたプライオリティーキューです
CODE:
template >
class IndexedPriorityQueue{
public:
//アクセサ:これを利用して順位変更や削除を行える。イテレーターと異なり捜査能力や参照能力は付加されていない
class Accessor;
public:
//コンストラクタは特に何もない
IndexedPriorityQueue();
//要素をキューに追加する
Accessor push(const ItemType& item);
//アクセサを用いて要素の優先順位を再変更する
void change_priority(const ItemType& item, const Accessor& a);
//アクセサを用いて要素を削除する
void erase(const Accessor& a);
//もっとも優先順位の高い要素を削除する
void pop();
//もっとも優先順位の高い要素の参照を得る
ItemType& top();
const ItemType& top() const;
//キューのサイズを得る
unsigned int size() const;
//キューが空かどうか
bool empty() const;
};
見える部分だけだとあんな感じですが、実際にはこうなってます
► スポイラーを表示
CODE:
#include
#include
#include
template >
class IndexedPriorityQueue{
class AccessorSubstance;
public:
//アクセサ:これを利用して順位変更や削除を行える。イテレーターと異なり捜査能力や参照能力は付加されていない
typedef std::shared_ptr Accessor;
public:
//コンストラクタは特に何もない
IndexedPriorityQueue();
//要素をキューに追加する
Accessor push(const ItemType& item);
//アクセサを用いて要素の優先順位を再変更する
void change_priority(const ItemType& item, const Accessor& a);
//アクセサを用いて要素を削除する
void erase(const Accessor& a);
//もっとも優先順位の高い要素を削除する
void pop();
//もっとも優先順位の高い要素の参照を得る
ItemType& top();
const ItemType& top() const;
//キューのサイズを得る
unsigned int size() const;
//キューが空かどうか
bool empty() const;
//------------------------------------------------------------------------------------------------
//以下はプライベート
//------------------------------------------------------------------------------------------------
private:
//ヒープを下から上に操作する(特定の要素をヒープの上層へ持っていく)
void UpHeap(unsigned int k);
//ヒープを上から下に操作する(特定の要素をヒープの下層へ持っていく)
void DownHeap(unsigned int k);
//アクセサの実態
class AccessorSubstance{
friend IndexedPriorityQueue;
#if DEBUG || _DEBUG
//デバグ時はアクセサのチェックを行う
//親を保存しアクセサ使用時にチェック
IndexedPriorityQueue* m_Parent;
//アクセサが有効な値を示しているか?
bool m_isValid;
#endif
unsigned int m_Index;
//アクセサそのものはコピーできない
AccessorSubstance& operator=(const AccessorSubstance& other);
AccessorSubstance(const AccessorSubstance& other);
#if DEBUG || _DEBUG
//デバグ用コンストラクタ
AccessorSubstance(unsigned int k, IndexedPriorityQueue* parent):m_Index(k),m_isValid(true),m_Parent(parent){
}
#else
//コンストラクタ
AccessorSubstance(unsigned int k):m_Index(k){
}
#endif
};
//実際に値を格納する構造体
struct Unit{
ItemType m_Item;
//アクセサへのウィークポインタを保存する(順位変更時にアクセサが生きていた場合そのインデックスを変更できるようするため)
std::weak_ptr m_Accessor;
};
//比較関数
Comp m_Comp;
//ヒープ本体
std::vector m_Heap;
//要素数
unsigned int m_Counter;
};
template
IndexedPriorityQueue::IndexedPriorityQueue():m_Counter(0){
}
template
typename IndexedPriorityQueue::Accessor IndexedPriorityQueue::push(const ItemType& item){
#if DEBUG || _DEBUG
//デバグ時にはインデックスのほかに自身のポインタを渡して他のキューと併用できなくする
Accessor acc(new AccessorSubstance(++m_Counter, this) );
#else
//デバグでなければポインタチェックを行わない
Accessor acc(new AccessorSubstance(++m_Counter) );
#endif
//アイテムを作成
Unit u = {item, std::weak_ptr(acc)};
//ヒープに追加して
m_Heap.push_back(u);
//ヒープを修復する
UpHeap(m_Counter);
return acc;
}
template
void IndexedPriorityQueue::change_priority(const ItemType& item, const Accessor& a){
//デバグ用のチェック機構
#if DEBUG || _DEBUG
if(this != a->m_Parent || !a->m_isValid || a->m_Index > m_Counter) throw(std::runtime_error("不正なアクセサ") );
#endif
int index = a->m_Index;
//変更前の値を保存して
ItemType& value = m_Heap[index-1].m_Item;
if(m_Comp(value, item)){
//値を前と比較して前のほうが優先順位が高ければ
m_Heap[index-1].m_Item = item;
//ヒープを下に操作して優先順位を下げる
DownHeap(index);
}else{
//そうでなければ
m_Heap[index-1].m_Item = item;
//ヒープを上に操作して優先順位を上げる
UpHeap(index);
}
}
template
void IndexedPriorityQueue::erase(const Accessor& a){
#if DEBUG || _DEBUG
if(this != a->m_Parent || !a->m_isValid || a->m_Index > m_Counter) throw(std::runtime_error("不正なアクセサ") );
a->m_isValid = false;
#endif
//削除する要素と最後尾を入れ替えて末尾を削除。ヒープを修復する
unsigned int index = a->m_Index;
m_Heap[index-1] = m_Heap[--m_Counter];
m_Heap.pop_back();
DownHeap(index);
}
template
void IndexedPriorityQueue::pop(){
//アルゴリズムはeraseと同じ。先頭と末尾を入れ替えて末尾を削除。その後ヒープを修復
unsigned int index = 1;
#if DEBUG || _DEBUG
if(auto ptr = m_Heap[index-1].m_Accessor.lock() ) ptr->m_isValid = false;
#endif
m_Heap[index-1] = m_Heap[--m_Counter];
m_Heap.pop_back();
if(empty()) return;
DownHeap(index);
}
template
const ItemType& IndexedPriorityQueue::top() const{
return m_Heap[0].m_Item;
}
template
ItemType& IndexedPriorityQueue::top(){
return m_Heap[0].m_Item;
}
template
unsigned int IndexedPriorityQueue::size() const{
return m_Counter;
}
template
bool IndexedPriorityQueue::empty() const{
return m_Counter == 0;
}
template
void IndexedPriorityQueue::DownHeap(unsigned int k){
//ヒープを上から下に操作する(引数kのインデックスの要素が実際よりも低いためヒープの下層へ移動する)
unsigned int temp = k;
//要素を保存
Unit u = m_Heap[k-1];
//この要素は親の2倍のインデックスと二倍+1の二つ
unsigned int j = k m_Index = k;
k = j;
j = k m_Index = k;
}
return;
}
template
void IndexedPriorityQueue::UpHeap(unsigned int k){
//今度はヒープを下から上に操作する
unsigned int temp = k;
Unit u = m_Heap[k-1];
//親の要素はkの半分のインデックスを持っている
unsigned int j = k >> 1;
while( j > 0 ){
if(!m_Comp(u.m_Item, m_Heap[j-1].m_Item)) break;
m_Heap[k-1] = m_Heap[j-1];
if(auto ptr = m_Heap[k-1].m_Accessor.lock() ) ptr->m_Index = k;
k = j;
j = k >> 1;
}
if(k != temp){
m_Heap[k-1] = u;
if(auto ptr = m_Heap[k-1].m_Accessor.lock() ) ptr->m_Index = k;
}
return;
}
こんな感じで使えます
CODE:
#include
#include
using namespace std;
int main(){
//テンプレート引数には関数オブジェクトを使う(デフォルトは昇順)
IndexedPriorityQueue> q;
//10へのアクセサを取得(以降長いのでautoを使う)
IndexedPriorityQueue>::Accessor acc10 = q.push(10);
//アクセサは破棄しても構わない
q.push(8);
q.push(7);
q.push(9);
//6へのアクセサを取得
auto acc6 = q.push(6);
q.push(2);
//4へのアクセサを取得
auto acc4 = q.push(4);
q.push(5);
q.push(-3);
q.push(2);
q.push(1);
//10を-10へ変更して並び替え
q.change_priority(-10,acc10);
//6を削除
q.erase(acc6);
//4へのアクセサをコピーして
auto acc4Copy = acc4;
//acc4で順位変更を行っても
q.change_priority(-4,acc4);
//コピーしたアクセサもちゃんと動作する。
q.change_priority(-40,acc4Copy);
while(!q.empty() ){
cout << q.top() << endl;
q.pop();
}
return 0;
}
出力は9,8,7,5,2,2,1,-3,-10,-40となり降順に値を取り出せます。
どの操作もO(logN)で行えます。(topとsizeとemptyは定数時間)
見ての通りアクセサを破棄すればほとんどstd::priority_queueと同じ動作をします。
探索アルゴリズムのダイクストラ法で若干活躍しますw
そしてC++0x早く来てほしい。
<追記>あ、若干コメントに不備があったので直しました