C++でジグザグイテレータを実装する方法をわかりやすく解説
ジグザグイテレータとは?
2つの1次元配列が与えられたとき、それぞれの要素を交互に順番に返すイテレータを実装することを考えます。このイテレータには、次の2つのメソッドが必要です。
- next() — 次の要素を取得します。
- hasNext() — 次の要素がまだ存在するかどうかを判定します。
例えば、入力が v1 = [1,2]、v2 = [3,4,5,6] の場合、出力は [1,3,2,4,5,6] となります。つまり、v1 と v2 から交互に1つずつ要素を取り出し、片方が尽きた後は残りの配列の要素をそのまま返していきます。
解法のアプローチ:キューを使った実装
この問題は、キュー(queue)とペア(pair)を組み合わせることで、簡潔かつ効率的に解くことができます。ペアの第1要素には「配列内の現在位置(インデックス)」、第2要素には「どちらの配列かを示す識別子(0ならv1、1ならv2)」を格納します。
アルゴリズムの手順
- ペアを格納するキュー
qを定義します。 - コンストラクタで2つの配列 v1 と v2 を受け取り、メンバ変数にコピーします。
- v1 のサイズが0より大きい場合は、
{0, 0}をキューに挿入します。 - v2 のサイズが0より大きい場合は、
{0, 1}をキューに挿入します。
next() メソッドの処理
- ペア型の一時変数
tempを定義し、キューの先頭要素を取得してキューから削除します。 - 戻り値用の変数
retを0で初期化します。 temp.secondが 1 の場合(v2側の要素):ret := v2[temp.first]として値を取得temp.firstを1増やすtemp.firstが v2 のサイズ未満であれば、tempをキューに再度挿入
- それ以外の場合(v1側の要素):
ret := v1[temp.first]として値を取得temp.firstを1増やすtemp.firstが v1 のサイズ未満であれば、tempをキューに再度挿入
retを返します。
hasNext() メソッドの処理
キューが空でない場合に true を返します。これにより、まだ取り出せる要素が残っているかどうかを簡単に判定できます。
C++による実装例
それでは、実際のコードを見てみましょう。
#include <bits/stdc++.h>
using namespace std;
class ZigzagIterator {
public:
queue<pair<int, int>> q;
vector<int> v1, v2;
ZigzagIterator(vector<int>& v1, vector<int>& v2) {
this->v1 = v1;
this->v2 = v2;
if (v1.size()) {
q.push({ 0, 0 });
}
if (v2.size()) {
q.push({ 0, 1 });
}
}
int next() {
pair<int, int> temp;
temp = q.front();
q.pop();
int ret = 0;
if (temp.second == 1) {
ret = v2[temp.first];
temp.first++;
if (temp.first < v2.size())
q.push(temp);
}
else {
ret = v1[temp.first];
temp.first++;
if (temp.first < v1.size())
q.push(temp);
}
return ret;
}
bool hasNext() {
return !q.empty();
}
};
main(){
vector<int> v1 = {1,3,5,7}, v2 = {2,4,6,8,10,12,17};
ZigzagIterator ob(v1, v2);
cout << (ob.next()) << endl;
cout << (ob.next()) << endl;
cout << (ob.hasNext() ? "True" : "False") << endl;
cout << (ob.next()) << endl;
cout << (ob.next()) << endl;
cout << (ob.next()) << endl;
cout << (ob.hasNext() ? "True" : "False") << endl;
cout << (ob.next()) << endl;
cout << (ob.next()) << endl;
cout << (ob.next()) << endl;
cout << (ob.next()) << endl;
cout << (ob.hasNext() ? "True" : "False") << endl;
cout << (ob.next()) << endl;
cout << (ob.next()) << endl;
cout << (ob.hasNext() ? "True" : "False") << endl;
}
入力
{1,3,5,7},{2,4,6,8,10,12,17}出力
1
2
True
3
4
5
True
6
7
8
10
True
12
17
False
計算量の評価
- 時間計算量: next() および hasNext() の各呼び出しは O(1) で完了します。
- 空間計算量: キューには常に最大2つの要素しか保持されないため、追加の空間計算量は O(1) です(入力配列の保存分を除く)。
まとめ
ジグザグイテレータは、キューに「配列の識別子」と「現在のインデックス」のペアを管理させることで、2つの配列を交互に走査できます。この手法は、3つ以上の配列にも容易に拡張できる点が大きな魅力です。競技プログラミングやシステム設計の面接でも頻出のパターンなので、ぜひマスターしておきましょう。
-
C++での2次元行列のジグザグ(対角)トラバーサルの実装方法
問題の概要 この記事では、2次元行列(マトリックス)のすべての要素を対角線に沿った順序、いわゆる「ジグザグ(対角)トラバーサル」で出力する方法を解説します。 まず、具体例を使って問題を理解しましょう。次のような3×3の行列が与えられたとします。 1 2 3 4 5 6 7 8 9 出力 − 1 4 2 7 5 3 8 6 9 対角トラバーサルのパターン 行列をジグザグ形式で出力する際には、どのようなパターンで要素が並ぶのでしょうか。下の図のように、要素は左下から右上へ向かう斜めのラインごとに順番に出力されます。
-
【C++】二分木のジグザグ走査(ZigZag Traversal)を2つのスタックで実装する方法
この問題では、二分木(binary tree)が与えられ、その全ノードをジグザグ状(ZigZag)に出力することが求められます。 まず、具体例を使って問題を確認しましょう。 上記の二分木をジグザグ走査すると、各ノードは次の順序で出力されます。 3 5 1 8 7 0 4 1層目は左から右、2層目は右から左…というように、レベルが変わるごとに走査の向きが交互に反転するのがジグザグ走査の特徴です。 解法の考え方 この問題を解くには、二分木をレベル順(幅優先)で走査し、各レベルが終わるたびに走査の向きを反転させます。 ここでは、「現在のレベル用(c