C++でキュー内の最初の一意な整数を取得するFirstUniqueクラスの実装
整数のキューが与えられたとき、その中で重複していない(一意な)整数のうち最初に現れるものを取り出したい場面を考えてみましょう。この問題は、FirstUniqueというクラスを実装することで解決できます。
FirstUniqueクラスは次の2つの操作を提供します。
- コンストラクタ:初期状態のキューとなる整数の配列を受け取って初期化します。
- showFirstUnique():キューの中で最初に現れる一意な整数の値を返します。一意な整数が存在しない場合は
-1を返します。 - add(value):指定した値をキューに追加します。
動作例
例えば、[2, 3, 5] で初期化し、以下の順にメソッドを呼び出した場合を考えます。
showFirstUnique()add(5)showFirstUnique()add(2)showFirstUnique()add(3)showFirstUnique()
このとき、出力はそれぞれ 2, 2, 3, -1 となります。
- 初期状態では
2が最初の一意な整数です。 5を追加しても2のままです。2を追加すると2が重複するため、次の一意な整数である3が答えになります。3を追加するとすべての値が重複するため、-1を返します。
アルゴリズムの考え方
この問題を解くために、キューとマップ(カウンタ)を組み合わせたアプローチを使います。
- 整数用のキュー
qを用意します。 - 各値の出現回数を記録するマップ
cntを用意します。 - コンストラクタでは、渡された配列に対して以下を行います。
- まず全要素
iについてcnt[i]を1増やします。 - 次にもう一度全要素を走査し、
cnt[i]が1(=まだ重複していない)であればqに挿入します。
- まず全要素
showFirstUnique()では以下を行います。- キューが空でなく、先頭要素の出現回数が1より大きい(=すでに重複している)間、先頭要素を削除し続けます。
- キューが空になった場合は
-1を、そうでなければ先頭要素を返します。
add(value)では以下を行います。cnt[value]を1増やします。cnt[value]が1(=初めて追加された値)であれば、valueをqに挿入します。
この方式のポイントは、重複した値を即座に削除せず、参照された時点で遅延的に除去することです。これにより、add() 操作は常に高速に処理でき、全体として効率的な実装になります。
C++での実装例
それでは、実際のC++コードを見てみましょう。
#include <bits/stdc++.h>
using namespace std;
class FirstUnique {
public:
queue <int> q;
map <int, int> cnt;
FirstUnique(vector<int>& nums) {
for (int i : nums) {
cnt[i]++;
}
for (int i : nums) {
if (cnt[i] == 1) {
q.push(i);
}
}
}
int showFirstUnique() {
while (!q.empty() && cnt[q.front()] > 1) q.pop();
return q.empty() ? -1 : q.front();
}
void add(int value) {
cnt[value]++;
if (cnt[value] == 1)
q.push(value);
}
};
main(){
vector<int> v = {2,3,5};
FirstUnique ob(v);
cout << (ob.showFirstUnique()) << endl;
ob.add(5);
cout << (ob.showFirstUnique()) << endl;
ob.add(2);
cout << (ob.showFirstUnique()) << endl;
ob.add(3);
cout << (ob.showFirstUnique()) << endl;
}
入力
{2,3,5}
ob.showFirstUnique();
ob.add(5);
ob.showFirstUnique();
ob.add(2);
ob.showFirstUnique();
ob.add(3);
ob.showFirstUnique();
出力
2 2 3 -1
計算量の評価
- 時間計算量:
add()はO(log n)(マップへの挿入)、showFirstUnique()は償却O(1)程度で動作します。各要素はキューに高々1度追加され、高々1度削除されるためです。 - 空間計算量:キューとマップに最大でO(n)の領域が必要です。
このように、キューとハッシュマップ(ここでは map)を組み合わせることで、データの追加と「最初の一意な値の参照」を効率よく両立できるのが、この実装の大きな特徴です。
-
C++で質素数(Frugal Number)を判定する方法【サンプルコード付き】
この記事では、正の整数 N が与えられたときに、その数が質素数(Frugal Number)であるかどうかを判定するプログラムを C++ で作成する方法を解説します。 質素数とは? 質素数(FRUGAL NUMBER)とは、その数自身の桁数が、素因数分解による表現の桁数よりも厳密に大きい数のことです。 例:625 の場合 625 を素因数分解すると 54 となります。 625 自身の桁数:3 桁 54 の表現の桁数:2 桁 3 は 2 よりも厳密に大きいため、625 は質素数です。 最初のいくつかの質素数:125、128、243、256、343、512、625 など 問題を理解するための具
-
C++で五胞体数(ペンタトープ数)を求める方法
五胞体数とは? 五胞体数(ペンタトープ数)は、パスカルの三角形の第5の対角線上に現れる数列として知られています。この数列を定義するには、パスカルの三角形に少なくとも5つの数が必要となるため、数列の最初の数はパスカルの三角形の第4行である 1 4 6 4 1 から始まります。 本チュートリアルでは、n番目の五胞体数を求める方法を解説します。まずは具体的な例を見てみましょう。 入力 : 1出力 : 1入力 : 4出力 : 35 以下の図から出力を確認できます。 この問題は数列に関するものなので、解法ではまず数列のパターンを見つけることから始めます。 解法のアプローチ このプログラムでは、数列の