C++で文字列内の子音をカウントする方法(反復処理と再帰処理の2つのアプローチ)
任意の長さの文字列 str が与えられ、その文字列に含まれる子音の数を「反復処理(イテレーション)」と「再帰処理」の2つの方法で求めることが課題です。
子音とは
子音とは母音以外のアルファベットのことで、a、i、e、o、u を除くすべてのアルファベットが子音とみなされます。したがって、このプログラムでは、文字列からこれら5つの母音以外のアルファベットを数え上げる必要があります。
再帰と反復の違い
再帰と反復は、どちらも一連の命令を繰り返し実行する仕組みです。再帰とは、関数内の処理が自分自身を繰り返し呼び出すことを指します。一方、反復とは、制御条件が偽(false)になるまでループが繰り返し実行されることを指します。
両者の主な違いは、再帰が常に「関数」に適用されるプロセスであるのに対し、反復は繰り返し実行したい「一連の命令」に適用されるという点です。
実行例
入力 − string str = "tutorials point" 出力 − count is 8
説明 − 与えられた文字列 str には、t、t、r、l、s、p、n、t の合計8個の子音が含まれています。
入力 − string str = "a e io u" 出力 − count is 0
説明 − 与えられた文字列 str には子音が一切含まれず、母音のみで構成されているため、カウントは0になります。
方法1:反復処理
プログラムのアプローチ
文字列を変数 str として受け取る
length() 関数を使って文字列の長さを取得する(文字列内の文字数が整数値として返される)
カウントを格納するための一時変数を用意する
i を0から文字列の長さ未満までループさせる
ループ内で str[i] が子音かどうかを判定し、子音であればカウントを1増やす
カウントを返す
結果を出力する
サンプルコード
// 反復方式のC++プログラム
#include <iostream>
using namespace std;
// 子音を判定する関数
bool consonant(char ch){
// 大文字・小文字の違いに対応
ch = toupper(ch);
return !(ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U') && ch >= 65 && ch <= 90;
}
// 子音をカウントする関数
int countconsonants(string s){
int result = 0;
for (int i = 0; i < s.length(); i++){
// 文字が子音かどうかを確認
if (consonant(s[i])){
++result;
}
}
return result;
}
// main関数
int main(){
string s = "wx abc def";
cout <<"count is: "<<countconsonants(s);
return 0;
}出力
上記のコードを実行すると、次の出力が得られます。
count is: 6
方法2:再帰処理
プログラムのアプローチ
文字列を変数 str として受け取る
length() 関数を使って文字列の長さを取得する
カウントを格納するための一時変数を用意する
文字列内の子音を数えるために自分自身を呼び出す再帰関数を作成する
サイズが1の場合は str[0] の子音判定結果を返す(基本ケース)
それ以外の場合は、再帰呼び出し (str, size-1) の結果に、最後の文字 (str[size-1]) の子音判定結果を加えて返す
サンプルコード
// 再帰方式のC++プログラム
#include <iostream>
using namespace std;
// 子音を判定する関数
bool consonant(char ch){
// 大文字・小文字の違いに対応
ch = toupper(ch);
return !(ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U') && ch >= 65 && ch <= 90;
}
// 子音の総数をカウントする関数
int consonantcount(string str, int n){
if (n == 1){
return consonant(str[0]);
}
return consonantcount(str, n - 1) +
consonant(str[n-1]);
}
int main(){
string str = "wx abc def";
cout <<"count is: "<<consonantcount(str, str.length());
return 0;
}出力
上記のコードを実行すると、次の出力が得られます。
count is: 6
まとめ
どちらの方法でも同じ結果が得られますが、反復処理はループで単純に文字を走査するためメモリ効率が良く、再帰処理はコードが簡潔になる反面、呼び出しごとにスタックフレームが積まれるため、長い文字列ではスタックオーバーフローのリスクがあります。用途や文字列の長さに応じて使い分けるとよいでしょう。
-
C++で二分木の完全ノードを数える方法(反復法と再帰法)
本記事では、二分木に含まれる「完全ノード(フルノード)」の数を、反復法と再帰法の2つのアプローチで求める方法を解説します。完全ノードとは、左と右の子を両方持ち、どちらの子もNULLでないノードのことです。つまり、ちょうど2つの子を持つノードのみが完全ノードとして扱われます。 二分木はデータの格納に用いられる特殊なデータ構造です。「各ノードが最大2つの子までしか持てない」という制約があり、ソート済み配列並みの高速な検索性能と、連結リスト並みの高速な挿入・削除性能を兼ね備えているのが特徴です。なお、1つ以上の子を持つ非葉ノードは「親ノード」とも呼ばれます。 二分木の基本構造は以下の通りです。
-
C++で二分木の半ノード(ハーフノード)を数える方法【反復・再帰の両アプローチ】
本記事では、二分木(バイナリツリー)に含まれる「半ノード(ハーフノード)」の数を、反復処理と再帰処理の2つのアプローチで求める方法を解説します。半ノードとは?半ノードとは、子を1つだけ持ち、もう片方の子がNULL(空)になっているノードのことです。なお、子をまったく持たない葉ノードは半ノードには含まれない点に注意してください。二分木はデータの格納に使われる特殊なデータ構造です。各ノードが最大2つの子を持つという制約があり、ソート済み配列並みの高速な検索と、連結リスト並みの高速な挿入・削除の両方を実現できるというメリットがあります。二分木の基本的な構造は以下の通りです。具体例入力:出力: カウン