C++で優先キュー(プライオリティキュー)を実装する方法
通常のキュー(待ち行列)は、FIFO(First-In-First-Out:先入れ先出し)方式で動作するデータ構造です。要素の挿入は一方の端(末尾)から行い、削除はもう一方の端(先頭)から行います。そのため、最初に入った要素が必ず最初に取り出されます。
キューの基本操作
- EnQueue(int data):末尾に要素を挿入する
- int DeQueue():先頭から要素を削除する
一方、優先キュー(プライオリティキュー)はFIFOの原則には従いません。代わりに、各要素が「緊急度」に応じた優先度を持ちます。
- 同じ優先度を持つ要素同士は、FIFO(先入れ先出し)の順序で処理されます。
- 優先度の高い要素は、優先度の低い要素よりも先に処理されます。
クラスの設計方針
ここでは、連結リストを用いて優先キューを実装します。クラスPriority_Queueの擬似コードによる設計概要は以下の通りです。
Begin
クラス Priority_Queue は以下の関数を持つ:
関数 insert():優先度付きで要素を優先キューに挿入する
1) キューが空の場合、データをキューの先頭に挿入する。
2) キューに既存ノードがある場合、新しいノードと同じ優先度を持つ
ノード群の末尾に、かつ新しいノードより優先度が低いすべての
ノードの手前に挿入する。
関数 del():キューから要素を削除する
キューが完全に空の場合はアンダーフローを表示し、
そうでなければ先頭要素を削除してフロントポインタを更新する。
End
C++による実装例
以下のコードでは、優先度の小さい値ほど高い優先度として扱われ、キューの先頭に近い位置へ挿入されます。メニュー形式の対話型プログラムとして、挿入・削除・表示・終了の操作を選択できます。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
struct n // ノードの宣言 {
int p; // 優先度
int info; // データ本体
struct n *l; // 次のノードへのポインタ
};
class Priority_Queue {
private:
// 先頭ポインタ f を宣言し、NULL で初期化
n *f;
public:
Priority_Queue() // コンストラクタ {
f = NULL;
}
void insert(int i, int p) {
n *t, *q;
t = new n;
t->info = i;
t->p = p;
if (f == NULL || p < f->p) {
// 空のキュー、または最高優先度なら先頭に挿入
t->l = f;
f = t;
} else {
// 挿入位置を探索
q = f;
while (q->l != NULL && q->l->p <= p)
q = q->l;
t->l = q->l;
q->l = t;
}
}
void del() {
n *t;
if (f == NULL) // キューが空の場合
cout<<"Queue Underflow\n";
else {
t = f;
cout<<"Deleted item is: "<<t->info<<endl;
f = f->l;
delete t;
}
}
void show() // キューの内容を表示 {
n *ptr;
ptr = f;
if (f == NULL)
cout<<"Queue is empty\n";
else {
cout<<"Queue is :\n";
cout<<"Priority Item\n";
while(ptr != NULL) {
cout<<ptr->p<<" "<<ptr->info<<endl;
ptr = ptr->l;
}
}
}
};
int main() {
int c, i, p;
Priority_Queue pq;
do // switch文による操作の実行 {
cout<<"1.Insert\n";
cout<<"2.Delete\n";
cout<<"3.Display\n";
cout<<"4.Exit\n";
cout<<"Enter your choice : ";
cin>>c;
switch(c) {
case 1:
cout<<"Input the item value to be added in the queue : ";
cin>>i;
cout<<"Enter its priority : ";
cin>>p;
pq.insert(i, p);
break;
case 2:
pq.del();
break;
case 3:
pq.show();
break;
case 4:
break;
default:
cout<<"Wrong choice\n";
}
}
while(c != 4);
return 0;
}
実装のポイント
- insert():新規ノードは、自分より優先度が高いノードの後ろ・同じ優先度のノードの末尾に配置されます。これにより、常に先頭から優先度順に並んだ状態が保たれます。
- del():先頭ノードを取り出して削除するため、最も優先度の高い要素が常に最初に取得されます。
- show():先頭から順に「優先度 データ」の形式で全要素を表示します。
実行結果
1.Insert 2.Delete 3.Display 4.Exit Enter your choice : 1 Input the item value to be added in the queue : 7 Enter its priority : 2 1.Insert 2.Delete 3.Display 4.Exit Enter your choice : 1 Input the item value to be added in the queue : 6 Enter its priority : 1 1.Insert 2.Delete 3.Display 4.Exit Enter your choice : 1 Input the item value to be added in the queue : 3 Enter its priority : 3 1.Insert 2.Delete 3.Display 4.Exit Enter your choice : 1 Input the item value to be added in the queue : 4 Enter its priority : 3 1.Insert 2.Delete 3.Display 4.Exit Enter your choice : 3 Queue is : Priority Item 1 6 2 7 3 3 3 4 1.Insert 2.Delete 3.Display 4.Exit Enter your choice : 4
この実行例では、優先度1の「6」が最も先に並び、続いて優先度2の「7」、そして同じ優先度3を持つ「3」と「4」が挿入された順序で並んでいることが確認できます。これは、優先キューが「優先度順・同一優先度内ではFIFO」という規則に従って動作していることを示しています。
-
シーザー暗号を実装するC++プログラム
シーザー暗号とは シーザー暗号は、平文の各文字を別の文字に置き換えることで暗号文を作り出す「単一換字式暗号(モノアルファベット暗号)」の一種です。換字式暗号の中でも最も基本的でシンプルな方式とされています。 この暗号方式は、一般的に「シフト暗号」とも呼ばれます。その考え方は、各アルファベットを0〜25の範囲内の固定した数だけ「ずらした」別のアルファベットに置き換えるというものです。 この方式では、送信者と受信者があらかじめ「秘密のシフト数」を共有しておきます。この0〜25の間の数値が、暗号化の鍵(キー)として機能します。 特に「3文字ずらす」場合には、このシフト暗号を指して「シーザー暗号」と
-
C++でAVL木(AVLツリー)を実装する方法:回転操作とサンプルコードを徹底解説
AVL木とは AVL木(AVL Tree)は、自己平衡型二分探索木(Self-balancing Binary Search Tree)の一種です。すべてのノードにおいて、左部分木と右部分木の高さの差が「1以下」に保たれるという性質を持っています。この平衡条件により、木が片側に偏って成長することを防ぎ、検索・挿入・削除といった操作を常に効率的(O(log n))に行うことができます。 木の回転(Tree Rotation)とは 木の回転とは、要素の順序(ソート順)を崩すことなく木の構造を変更する操作のことです。あるノードを一段上へ移動させ、別のノードを一段下へ移動させることで実現されます。 回