C++で有効な括弧文字列を判定する方法(スタックを使ったバランスチェック)
プログラミングにおいて、式の中に含まれる括弧が正しく対応しているか(バランスしているか)を判定することは、構文解析などでよく使われる基本的な処理です。対象となる括弧は ()、{}、[] の3種類です。
例えば、"()[(){()}]" はすべての括弧が正しく入れ子になっているため有効な文字列ですが、"{[}]" は閉じ括弧の順序が対応していないため無効です。
アルゴリズムの考え方
この問題はスタック(stack)を使うことで効率的に解決できます。手順は以下の通りです。
- 式の文字列を先頭から順に走査します。
- 現在の文字が開き括弧(
(、{、[)であれば、スタックにプッシュします。 - 現在の文字が閉じ括弧(
)、}、])であれば、スタックからポップします。 - ポップした括弧が、現在の閉じ括弧に対応する開き括弧であれば問題ありません。対応していない場合は、その文字列はバランスしていません。
- 現在の文字が開き括弧(
- 文字列の走査が終わった後、スタックに開き括弧が残っている場合は、閉じられていない括弧が存在するため、その文字列はバランスしていません。
計算量
このアルゴリズムの時間計算量は O(n)、空間計算量もスタックに最大で文字列の長さ分の要素を保持するため O(n) となります。
C++での実装例
以下に、実際のC++コードを示します。
#include <iostream>
#include <stack>
using namespace std;
bool isBalancedExp(string exp) {
stack<char> stk;
char x;
for (int i = 0; i < exp.length(); i++) {
// 開き括弧の場合はスタックにプッシュ
if (exp[i] == '(' || exp[i] == '[' || exp[i] == '{') {
stk.push(exp[i]);
continue;
}
// 開き括弧が残っていないのに閉じ括弧が来た場合は不成立
if (stk.empty())
return false;
switch (exp[i]) {
case ')':
x = stk.top();
stk.pop();
if (x == '{' || x == '[')
return false;
break;
case '}':
x = stk.top();
stk.pop();
if (x == '(' || x == '[')
return false;
break;
case ']':
x = stk.top();
stk.pop();
if (x == '(' || x == '{')
return false;
break;
}
}
// 最後にスタックが空であればバランスしている
return (stk.empty());
}
int main() {
string expresion = "()[(){()}]";
if (isBalancedExp(expresion))
cout << "This is Balanced Expression";
else
cout << "This is Not Balanced Expression";
}
入力
"()[(){()}]"
出力
This is Balanced Expression
まとめ
括弧の対応チェックは、スタックのLIFO(後入れ先出し)という性質を活かすことで、直感的かつ効率的に実装できます。この手法は、コンパイラの構文解析やエディタのコード補完機能など、さまざまな場面で応用されている重要なアルゴリズムです。
-
C++で数独の有効性を判定するアルゴリズムを解説
9×9の行列で表される数独(Sudoku)が与えられたとします。この課題の目的は、与えられた数独の配置が有効かどうかを判定することです。一般的な数独の盤面は次のようになります。数独のルール各行には1〜9の範囲の数字が入る各列には1〜9の範囲の数字が入る各3×3のブロックには重複のない数字が入る同じ行に同じ数字が現れることはできない同じ列に同じ数字が現れることはできない入出力の例入力例:sudoku[]= [[3,5,.,.,2,.,.,.,.] ,[7,.,.,1,6,5,.,.,.] ,[.,9,8,.,.,.,.,6,.] ,[8,.,.,.,6,.,.,.
-
C++で文字列をトークン化(分割)する2つの方法を解説
文字列のトークン化(分割)とは、1つの文字列を区切り文字(スペースやカンマなど)を基準に、複数の部分文字列へ分割する処理のことです。C++では、標準ライブラリだけでもいくつかの方法で実現できます。本記事では、代表的な2つの方法をサンプルコード付きで紹介します。方法1:stringstreamを使って空白で分割する1つ目の方法は、stringstreamを使ってスペースで区切られた単語を順に読み取る方法です。この方法はやや制限がありますが、適切なチェックを加えれば十分に目的を果たすことができます。サンプルコード#include <vector> #include <string