-
C++で二分木のルートから特定ノードまでの距離を求める方法
二分木が与えられたとき、ルートから特定のノード u までの距離(経路の長さ)を求める問題を考えてみましょう。例として、次のような二分木を想定します。この木において、ルートからノード6までの距離は2、ルートからノード8までの距離は3となります。解決のアプローチこの問題は、再帰的な手法を用いて解くことができます。具体的には、目的のノードを左部分木と右部分木の両方に対して再帰的に探索し、再帰の各段階(レベル)で距離を1ずつ加算していきます。探索の仕組みは以下の通りです。現在のノードがNULLの場合は -1 を返します(ノードが見つからなかったことを示す)。現在のノードの値が目的の値と一致した場合、ま
-
C++でn以下の素数の積を求める方法【エラトステネスの篩】
はじめに数値 n が与えられたとき、1 から n までの範囲に含まれる素数の積を求めることを考えてみましょう。例えば n = 7 の場合、2 × 3 × 5 × 7 = 210 となるため、出力は 210 になります。アルゴリズムの考え方この問題を解くには、まずエラトステネスの篩(ふるい)を使って 1 から n までのすべての素数を効率よく列挙し、その後、見つかった素数を順番に掛け合わせて積を計算します。エラトステネスの篩は、各素数の倍数を順次除外していくことで素数を判定する古典的な手法です。計算量が O(n log log n) と非常に効率的なため、ある程度大きな n に対しても高速に動作
-
C++で配列内の重複要素を検出する方法:O(n)時間・O(1)の追加メモリを実現する符号マーキング技法
はじめに0からn-1までの範囲の数値が格納された配列を考えます。このとき、同じ数値が複数回出現することがあります。追加のメモリ領域を使わずに、これらの重複している数値をすべて見つけるのが本記事の目的です。例えば、n = 7 の場合で配列が [5, 2, 3, 5, 1, 6, 2, 3, 4, 5] のとき、答えは 5, 2, 3 となります。アルゴリズムの考え方:符号マーキング技法この問題を解く鍵となるのは「符号マーキング(sign marking)」というテクニックです。配列の各値が必ず 0 ~ n-1 の範囲に収まっているため、その値自体を配列のインデックスとして利用できます。具体的には
-
C++で偶数番目と奇数番目の桁の合計を求める方法
偶数番目と奇数番目の桁の合計とは整数 N が与えられたとき、左から数えて奇数番目の桁の数字の合計と、偶数番目の桁の数字の合計をそれぞれ求めることを考えます。例えば、数値が 153654 の場合、odd_sum(奇数番目の合計)は 9、even_sum(偶数番目の合計)は 15 となります。具体的には、奇数番目の桁は 1 + 3 + 5 = 9、偶数番目の桁は 5 + 6 + 4 = 15 となります。解法のアプローチこの問題を解くには、最下位の桁から順にすべての桁を取り出していきます。ここで重要なポイントは、元の数値の桁数が奇数であれば最下位の桁は奇数番目の位置にあり、桁数が偶数であれば偶数番
-
C++でソート済み配列から出現頻度がn/2以上の要素を検索する方法
サイズnのソート済み配列を考えます。この配列には、出現回数がn/2以上(nは要素数)となる要素が必ず1つ存在します。例えば、配列が [3, 4, 5, 5, 5] の場合、出力は 5 となります。解法のポイントこの種の配列を注意深く観察すると、重要な性質に気づくことができます。それは、出現頻度がn/2以上の要素は、必ずインデックス n/2 の位置にも存在するという点です。なぜなら、ある要素が配列全体の半分以上を占めている場合、その要素は配列の中央位置(n/2)に必ず含まれるからです。したがって、配列がすでにソートされていることを利用すれば、線形探索やハッシュマップを使わずに、単一の要素アクセス
-
C++で3つのソート済み配列から最も近い3つの要素を効率的に検索する方法
3つのソート済み配列 A、B、C が与えられたとき、それぞれから要素 A[i]、B[j]、C[k] を選び、max(|A[i] − B[j]|、|B[j] − C[k]|、|C[k] − A[i]|) が最小になるような組み合わせを見つける問題を考えてみましょう。例えば、A = [1, 4, 10]、B = [2, 15, 20]、C = [10, 12] の場合、答えは A から 10、B から 15、C から 10 の3つの要素となります。配列 A、B、C のサイズをそれぞれ p、q、r とします。この問題は、配列がすでにソートされていることを利用して、3つのポインタを動かしながら線形時間
-
C++でa + b = c + dとなる4つの配列要素(a、b、c、d)を見つける方法
問題の概要整数のリストが与えられたとき、(a, b) と (c, d) という2組のペアを構成する4つの異なる整数を見つけ、a + b = c + d が成り立つようにすることが課題です。条件を満たす答えが複数存在する場合は、そのうち1つだけを出力すれば十分です。例えば、配列の要素が A = [7, 5, 9, 3, 6, 4, 2] の場合、(7, 3) と (6, 4) のようなペアが考えられます(どちらも合計は10)。解法のアプローチ:ハッシュマップの活用この問題はハッシュ(連想配列)のテクニックを使うことで効率的に解けます。ハッシュテーブルには、ペアの合計値をキーとして、対応するインデ
-
C++でO(n)未満の時間で範囲制限された配列内の各要素の頻度を求める方法
整数が格納された配列A(サイズn)が与えられているとします。この課題は、O(n)未満の計算量で配列内のすべての要素の出現頻度を求めることです。ただし、要素の値はあらかじめある上限値M未満に制限されているものとします。 この問題は、配列がソート済み(昇順)であることを前提とすれば、二分探索(バイナリサーチ)の考え方を応用することで効率的に解けます。 アルゴリズムの基本的な考え方 ここでは分割統治法にもとづく再帰的なアプローチを採用します。手順は以下のとおりです。 着目している区間の両端の要素を比較します。 両端の要素が一致している場合、配列はすでにソート済みであるため、その区間内のすべての
-
C++でGCD(a^n, c)を求める方法 ― a、n、cが1〜10^9の範囲の場合
本記事では、片方の数が (10^9)^(10^9) にもなり得るような巨大な2つの数の最大公約数(GCD)を求める方法を解説します。この規模の数値は、long型をはじめとする標準的なデータ型には格納できません。例えば、a = 10248585、n = 1000000、b = 12564 の場合、GCD(a^n, b) の結果は 9 になります。数が非常に大きいため、通常のユークリッドの互除法を直接適用することはできません。そこで、O(log n) の計算量で実行できる「繰り返し二乗法(modular exponentiation)」を活用します。解法の考え方ここで重要となるのが、次の性質です。
-
C++で配列内に1文字だけ異なる文字列が存在するかどうかを確認する方法
文字列 s と、複数の文字列を格納した配列 A があるとします。このとき、配列の中に「元の文字列と長さが同じで、かつ1文字だけ異なる文字列」が存在するかどうかを判定する問題を考えてみましょう。 例えば、文字列が banana で、配列が [bana, orange, banaba, banapy] の場合を想定します。このうち banaba は banana と長さが同じで、1文字だけ異なるため、結果は true となります。 解決のアプローチ この問題は、以下の手順で解くことができます。 配列内の各文字列に対して、次の処理を繰り返します。 まず、その文字列の長さが s と同じかどうかを確認
-
C++で式に重複した括弧が含まれているかどうかを判定する方法
式 exp が与えられたとき、その式に重複した括弧が含まれているかどうかを確認する問題を考えてみましょう。ある部分式が2組以上の括弧で囲まれている場合、その式は重複した括弧を持つことになります。例えば、次のような式を見てください。(5+((7−3)))この例では、部分式 (7 − 3) が2組の括弧で囲まれているため、重複した括弧が存在します。解決アプローチ:スタックを使った判定この問題は、スタックを使うことで効率的に解くことができます。アルゴリズムの手順は以下の通りです。式内の各文字を先頭から順に走査します。文字が開き括弧「(」、演算子、またはオペランドである場合は、スタックにプッシュします
-
C++で指定した数が無限の等差数列に存在するかどうかを判定する方法
3つの整数 a、b、c が与えられたとします。初項を a、公差を c とする無限に続く等差数列を考えたとき、b がこの数列に含まれるかどうかを判定するのが本記事のテーマです。 例えば、a = 1、b = 7、c = 3 の場合、数列は「1, 4, 7, 10, …」となります。この中に 7 は含まれているため、出力は「Yes」になります。 判定の考え方 この問題は、次の2つのケースに分けて考えることで解くことができます。 c = 0 の場合: 数列のすべての項は a と等しくなります。したがって、a = b であれば「Yes」を出力し、a ≠ b であれば「No」を返します。 c >
-
【C++】文字列内に現れる部分文字列のすべての出現位置(インデックス)を検索する方法
問題の概要 文字列 str と、その中から探したい部分文字列 sub_str が与えられたとき、sub_str が str 内に出現するすべての位置(インデックス)を求めることを考えます。 例えば、str が「aabbababaabbbabbaaabba」、sub_str が「abb」である場合、出現位置は 1 9 13 18 となります。 解決のアプローチ:substr() 関数の活用 この問題は、C++ STL に用意されている substr() 関数を使うことで簡単に解決できます。substr() 関数は、第1引数に検索を開始する位置、第2引数に切り出す文字列の長さを指定すると、その
-
C++ですべての区間の共通部分(交差)を求めるアルゴリズム
問題の概要{L, R} という形式で表される N 個の区間があるとします。ここで、L は開始時刻、R は終了時刻を表します。このとき、与えられたすべての区間に共通して含まれる「交差(共通部分)」を求める必要があります。交差とは、すべての区間の内部に存在する区間のことであり、そのような区間が存在しない場合は -1 を返します。例えば、区間が [{1, 6}, {2, 8}, {3, 10}, {5, 8}] である場合、出力される区間は {5, 6} となります。解法のアプローチこの問題は、以下の手順で効率的に解くことができます。最初の区間を「最終的な交差区間」として初期化します。2番目の区間以
-
C++で合計がNとなる2の累乗の数をK個見つける方法
問題の概要2つの整数 N と K が与えられたとします。求めるのは、それぞれが 2 の累乗であり、かつ合計が N となる K 個の数値です。そのような組み合わせが存在しない場合は -1 を返します。例として、N = 9、K = 4 の場合を考えてみましょう。このとき出力は「4 2 2 1」となります。合計は 9、要素数は 4 であり、各要素がすべて 2 の累乗になっていることが確認できます。解法のアプローチこの問題は、優先度付きキュー(プライオリティキュー)を活用することで効率的に解くことができます。解法の手順は以下の通りです。K が N のセットビット数(2進表現で 1 になっているビットの
-
C++でa+b+c=dを満たす最大のdを配列から見つける方法
整数の集合が与えられたとき、d = a + b + c を満たす数 d を見つけ、その値を最大化することが目標です。ここで重要なのは、a、b、c、d のすべてが集合内に存在していなければならないという点です。集合の要素数は最小1個、最大1000個であり、各要素は有限の数であるものとします。例えば、集合が {2, 3, 5, 7, 12} の場合、12 = 2 + 3 + 7 と表現できるため、最大の d は 12 となります。解法のアプローチ:ハッシュテーブルを活用するこの問題は、ハッシュテーブル(ハッシュマップ)の手法を使うことで効率的に解くことができます。基本的な考え方は以下の通りです。ま
-
C++で配列要素の積の下位k桁を求める方法
n個の要素を持つ配列Aと数値kが与えられたとき、配列内のすべての要素の積の下位k桁を求める問題を考えてみましょう。例えば、A = [15, 22, 13, 19, 17] の場合、各要素の積は 1385670 となります。ここで k = 3 とした場合、求めるべき下位3桁は「670」です。解法のアプローチこの問題を効率的に解くには、要素を掛け合わせる際に 10k を法として剰余を取りながら計算します。通常、配列の要素数が増えると積は急激に巨大化し、整数型のオーバーフローを引き起こす可能性があります。しかし、10k で割った余りだけを保持すれば、積がどれほど大きくなっても、必要な下位k桁を正確に
-
C++で数値のビットを効率的に反転するプログラムの実装方法
はじめにこのチュートリアルでは、数値のビットを効率的に反転するプログラムについて解説します。非負の整数が与えられたとき、その数値を2進数形式に変換し、各ビットを反転(0を1に、1を0に)した後、反転結果の10進数の値を出力することを目的とします。例えば、17は2進数で「10001」と表されます。これを反転すると「01110」となり、10進数では14になります。アルゴリズムの考え方ビット反転を効率的に行うには、以下の手順に従います。最上位ビットの位置を求める: log2(n) を使って、数値 n の最上位ビット(最も左側の1)の位置 x を取得します。マスクを作成する: 1 を x ビット左シフ
-
C++で連結リストの長さを求める方法(反復処理と再帰処理の両アプローチ)
この記事では、反復(イテレーティブ)と再帰(リカーシブ)という2つの異なるアプローチを用いて、C++で連結リストの長さを求める方法を詳しく解説します。リストの先頭ポインタが与えられている場合、以下の手順に従うことでノード数を正確に取得できます。 反復アプローチの手順 リストの先頭ノードから処理を開始します。 現在のポインタがNULLでない間、次のノードへ移動し、そのたびにカウントを1増やしていきます。 ポインタがNULLになった時点でのカウント値が、リスト全体の長さとなります。 再帰アプローチの手順 先頭ノードを引数として関数に渡します。 基本条件(ベースケース)は「引数がNULLの
-
C++で配列データからヒストグラムを作成するプログラムの書き方
この記事では、C++を使って配列内のデータからヒストグラム(度数分布図)を作成するプログラムを解説します。 ここでは、配列に格納された整数値をもとに、縦軸・横軸の両方が配列の値と対応するヒストグラムをコンソール上に描画することを目標とします。 アルゴリズムの流れ ヒストグラムの描画は、以下の手順で行います。 最大値の取得:max_element関数を使って配列内の最大値を求めます。この値がヒストグラムの最上部(y軸の最大値)になります。 上の行から順に出力:最大値から0まで1ずつ減らしながら各行を処理し、その行の高さ(i)以上の値を持つ配列要素の位置に「x」を表示します。条件を満たさない位置