C++で直接アドレス指定テーブルを実装する方法|O(1)の挿入・検索・削除
この記事では、C++を使って直接アドレス指定テーブル(Direct Addressing Table)を実装する方法を解説します。直接アドレス指定テーブルは、各要素が普遍集合 S = {0, 1, ..., n−1} のいずれかのキーを持ち、n がそれほど大きくなく、すべてのキーが一意である場合に有効なデータ構造です。キーをそのまま配列の添字として扱うため、挿入・検索・削除をすべて O(1) の時間で行えるのが最大の特徴です。
基本となる操作と擬似コード
直接アドレス指定テーブルでは、次の3つの基本操作を定義します。
Begin
insert():
テーブル変数 word と key を引数として受け取る。
T[ x.key ] = x (x はキーの値)
delete():
テーブル変数 word と key を引数として受け取る。
T[ x.key ] = tab(0, "") // 空スロットで上書き
search():
T[key] を返す
End
C++での実装例
以下は、メニュー形式で挿入・削除・検索を試せる完全なサンプルプログラムです。テーブルのサイズは 65536 とし、空のスロットは tab(0, "") で表しています。
#include<iostream>
#include<cstdlib>
#include<string>
#include<cstdio>
using namespace std;
struct tab { // テーブルの変数宣言
string word;
int key;
tab()
{}
tab(int k, string w) // 変数を初期化するコンストラクタ
{
word = w;
key = k;
}
};
void INSERT(tab T[], tab x) {
T[ x.key ] = x;
}
void DELETE(tab T[], tab x) {
T[ x.key ] = tab(0, "");
}
tab SEARCH(tab T[], int key) {
return T[ key ];
}
int main() {
int i, k, c;
string str;
tab T[65536]; // テーブルのサイズを初期化
tab x;
for(i = 0; i < 65536; i++)
T[i] = tab(0,"");
while (1) {
cout<<"1.キーに要素を挿入する"<<endl;
cout<<"2.テーブルから要素を削除する"<<endl;
cout<<"3.テーブルから要素を検索する"<<endl;
cout<<"4.終了"<<endl;
cout<<"選択してください: ";
cin>>c;
switch(c) {
case 1: {
string str1 = "";
cout<<"キー値を入力してください: ";
cin>>k;
cout<<"挿入する文字列を入力してください: ";
cin.ignore();
getline(cin, str);
INSERT(T, tab(k, str));
break;
}
case 2:
cout<<"削除する要素のキーを入力してください: ";
cin>>k;
x = SEARCH(T, k);
DELETE(T, x);
break;
case 3:
cout<<"検索する要素のキーを入力してください: ";
cin>>k;
x = SEARCH(T, k);
if (x.key == 0) {
cout<<"そのキーには要素が挿入されていません"<<endl;
continue;
}
cout<<"キー "<<k<<" の要素 -> ";
cout<<"\""<<x.word<<"\""<<endl;
break;
case 4:
exit(1);
default:
cout<<"無効な選択です"<<endl;
}
}
return 0;
}
実行結果
1.キーに要素を挿入する 2.テーブルから要素を削除する 3.テーブルから要素を検索する 4.終了 選択してください: 1 キー値を入力してください: 1 挿入する文字列を入力してください: hi 1.キーに要素を挿入する 2.テーブルから要素を削除する 3.テーブルから要素を検索する 4.終了 選択してください: 1 キー値を入力してください: 2 挿入する文字列を入力してください: tutorials 1.キーに要素を挿入する 2.テーブルから要素を削除する 3.テーブルから要素を検索する 4.終了 選択してください: 1 キー値を入力してください: 3 挿入する文字列を入力してください: point 1.キーに要素を挿入する 2.テーブルから要素を削除する 3.テーブルから要素を検索する 4.終了 選択してください: 3 検索する要素のキーを入力してください: 1 キー 1 の要素 -> "hi" 1.キーに要素を挿入する 2.テーブルから要素を削除する 3.テーブルから要素を検索する 4.終了 選択してください: 3 検索する要素のキーを入力してください: 4 そのキーには要素が挿入されていません 1.キーに要素を挿入する 2.テーブルから要素を削除する 3.テーブルから要素を検索する 4.終了 選択してください: 2 削除する要素のキーを入力してください: 1 1.キーに要素を挿入する 2.テーブルから要素を削除する 3.テーブルから要素を検索する 4.終了 選択してください: 3 検索する要素のキーを入力してください: 1 そのキーには要素が挿入されていません 1.キーに要素を挿入する 2.テーブルから要素を削除する 3.テーブルから要素を検索する 4.終了 選択してください: 4
計算量と留意点
- 時間計算量: 挿入・検索・削除はいずれも配列への直接アクセスのみで行われるため、最悪でも O(1) です。
- 空間計算量: キーの取り得る範囲全体(この例では 65536 個)分の領域をあらかじめ確保する必要があるため、O(U) のメモリを消費します。
- 適用条件: キーが非負の整数で、範囲が比較的小さい場合に適しています。キーの範囲が広い場合や疎な場合は、ハッシュテーブルの方がメモリ効率に優れています。
- 空スロットの扱い: サンプルコードでは
key == 0を「要素が存在しない」ことの目印として利用しています。
-
C++でアフィン暗号を実装する方法|暗号化・復号化のコード解説
アフィン暗号とは? アフィン暗号(Affine Cipher)は、単一文字置換暗号(モノアルファベティック置換暗号)の一種です。アルファベットの各文字を数値に対応付け、シンプルな数学関数によって暗号化を行い、その結果を再び文字へと変換します。 サイズ m のアルファベットでは、まず各文字を 0 ~ m−1 の範囲の整数にマッピングします。 アフィン暗号の「鍵」は a と b の 2 つの数値から構成されます。このうち a は、m と互いに素(最大公約数が 1)である数を選ぶ必要があります。 暗号化の仕組み 暗号化では、平文の各文字に対応する整数を、合同算術(モジュロ演算)を用いて別の整数へ変換
-
シーザー暗号を実装するC++プログラム
シーザー暗号とは シーザー暗号は、平文の各文字を別の文字に置き換えることで暗号文を作り出す「単一換字式暗号(モノアルファベット暗号)」の一種です。換字式暗号の中でも最も基本的でシンプルな方式とされています。 この暗号方式は、一般的に「シフト暗号」とも呼ばれます。その考え方は、各アルファベットを0〜25の範囲内の固定した数だけ「ずらした」別のアルファベットに置き換えるというものです。 この方式では、送信者と受信者があらかじめ「秘密のシフト数」を共有しておきます。この0〜25の間の数値が、暗号化の鍵(キー)として機能します。 特に「3文字ずらす」場合には、このシフト暗号を指して「シーザー暗号」と