以下の問題の記述形式です。

基本情報技術者試験(科目B)の「擬似言語」の形式に基づいた、初学者向けの練習問題です。

上記資料にある記述ルール(配列の添字は1から始まるなど)に準拠し、それぞれの解答、解説、そしてJavaで書いた場合のコードを併記します。

解答解説はアコーディオン形式でクリックにより展開できます。


目次

第1問:変数の演算と代入

【問題】

次のプログラムを実行したとき、出力される ans の値はいくつか。

整数型: a ← 5
整数型: b ← 3
整数型: ans ← a × b + a
ans を出力

【解答】

20

【解説】

基本的な変数の代入と演算の問題です。

  1. a に 5、b に 3 が代入されます。
  2. a × b5 × 3 = 15 です。
  3. それに a (つまり 5) を足すので、15 + 5 = 20 となります。
  4. 結果として ans に 20 が代入され、出力されます。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        int a = 5;
        int b = 3;
        int ans = a * b + a;
        System.out.println(ans);
    }
}


第2問:条件分岐(if文)

【問題】

次のプログラムを実行したとき、出力される文字列は何か。

整数型: score ← 75
文字列型: result ← "不合格"

if (score ≥ 80)
  result ← "優"
elseif (score ≥ 60)
  result ← "良"
else
  result ← "不可"
endif

result を出力

【解答】

【解説】

条件分岐(if-elseif-else)の流れを追う問題です 1。

  1. 最初の条件 score ≥ 80(75は80以上か?)は False なので、次の elseif に進みます。
  2. 次の条件 score ≥ 60(75は60以上か?)は True です。
  3. 対応する処理 result ← "良" が実行されます。
  4. 条件が成立したため、以降の else は無視され、endif に抜けます。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        int score = 75;
        String result = "不合格";

        if (score >= 80) {
            result = "優";
        } else if (score >= 60) {
            result = "良";
        } else {
            result = "不可";
        }
        System.out.println(result);
    }
}


第3問:繰り返し処理(while文)

【問題】

次のプログラムを実行したとき、出力される count の値はいくつか。

整数型: i ← 1
整数型: count ← 0

while (i ≤ 10)
  count ← count + i
  i ← i + 3
endwhile

count を出力

【解答】

22

【解説】

while ループによる繰り返し加算です 2

  1. 1回目: i=1 (条件:真)。count = 0+1 = 1i は 1+3 = 4 になります。
  2. 2回目: i=4 (条件:真)。count = 1+4 = 5i は 4+3 = 7 になります。
  3. 3回目: i=7 (条件:真)。count = 5+7 = 12i は 7+3 = 10 になります。
  4. 4回目: i=10 (条件:真)。count = 12+10 = 22i は 10+3 = 13 になります。
  5. 5回目: i=13 (条件:偽)。ループを終了します。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        int i = 1;
        int count = 0;

        while (i <= 10) {
            count = count + i;
            i = i + 3;
        }
        System.out.println(count);
    }
}


第4問:配列とfor文(重要:添字の違い)

【問題】

次のプログラムを実行したとき、出力される total の値はいくつか。

※試験の仕様に基づき、配列の要素番号は1から始まるものとする。

整数型の配列: nums ← {10, 20, 30, 40, 50}
整数型: total ← 0
整数型: k

for (k を 1 から 3 まで 1 ずつ増やす)
  total ← total + nums[k]
endfor

total を出力

【解答】

60

【解説】

配列の先頭から3つの要素を足し合わせる処理です。

  1. k=1 のとき、nums[1]10total = 0 + 10 = 10。
  2. k=2 のとき、nums[2]20total = 10 + 20 = 30。
  3. k=3 のとき、nums[3] は 30。total = 30 + 30 = 60。ループ終了。

注意点:

科目Bの擬似言語では配列の添字(インデックス)が 1 から始まりますが、Javaでは 0 から始まります。Javaコードに変換する際は添字をずらす必要があります。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        int[] nums = {10, 20, 30, 40, 50};
        int total = 0;

        // Javaの配列は0番目から始まるため、
        // 擬似言語の「1から3まで」は「0から2まで」に対応します。
        for (int k = 0; k < 3; k++) { 
            total = total + nums[k];
        }
        System.out.println(total);
    }
}


第5問:関数の呼び出しと論理演算

【問題】

次のプログラム中の関数 checkValue(15) を呼び出したとき、戻り値として返される文字列は何か。

※演算子 mod は剰余(割り算の余り)を表す 4。

○文字列型: checkValue(整数型: n)
  if ((n mod 3 = 0) and (n mod 5 = 0))
    return "A"
  elseif (n mod 3 = 0)
    return "B"
  else
    return "C"
  endif

【解答】

"A"

【解説】

FizzBuzz問題のような論理演算です。引数 n は 15 です。

  1. 最初の条件 (n mod 3 = 0) and (n mod 5 = 0) を評価します。
  2. 15 mod 3 は 0(割り切れる)。
  3. 15 mod 5 は 0(割り切れる)。
  4. 両方とも成立(True)しているため、and 条件全体が真となり、return "A" が実行されます。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        System.out.println(checkValue(15));
    }

    public static String checkValue(int n) {
        // Javaでは「mod」の代わりに「%」、「and」の代わりに「&&」を使います
        if ((n % 3 == 0) && (n % 5 == 0)) {
            return "A";
        } else if (n % 3 == 0) {
            return "B";
        } else {
            return "C";
        }
    }
}

第6問:2次元配列のアクセス

【問題】

次のプログラムを実行したとき、出力される sum の値はいくつか。

※{{...}, {...}} は2次元配列を表し、mat[行][列] でアクセスする 2。

整数型の配列: mat ← {{1, 2, 3}, 
                     {4, 5, 6}, 
                     {7, 8, 9}}
整数型: sum ← 0
整数型: i

for (i を 1 から 3 まで 1 ずつ増やす)
  sum ← sum + mat[i][i]
endfor

sum を出力


【解答】

15

【解説】

正方形の行列(3x3)の対角線上の要素を足し合わせる処理です。

  1. i=1: mat[1][1]1sum = 0 + 1 = 1。
  2. i=2: mat[2][2]5sum = 1 + 5 = 6。
  3. i=3: mat[3][3] は 9。sum = 6 + 9 = 15。結果、15が出力されます。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        int[][] mat = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
        int sum = 0;
        // Javaは0始まりなので、mat[i][i]でアクセスするために0から2まで回す
        for (int i = 0; i < 3; i++) {
            sum = sum + mat[i][i];
        }
        System.out.println(sum);
    }
}


第7問:配列内の最大値探索

【問題】

次のプログラムを実行したとき、出力される maxVal の値はいくつか。

整数型の配列: scores ← {10, 50, 30, 20, 40}
整数型: maxVal ← scores[1]
整数型: i

for (i を 2 から scoresの要素数 まで 1 ずつ増やす)
  if (scores[i] > maxVal)
    maxVal ← scores[i]
  endif
endfor

maxVal を出力

【解答】

50

【解説】

配列の中から最大値を見つけるアルゴリズムです。

50

【解説】

配列の中から最大値を見つけるアルゴリズムです。

  1. 最初に maxValscores[1](つまり 10)を仮置きします。
  2. i=2: scores[2](50) と maxVal(10) を比較。50 > 10 なので maxVal50 に更新。
  3. i=3: scores[3](30) と maxVal(50) を比較。更新なし。
  4. 以降、20, 40 と比較しても 50 を超えないため更新なし。
  5. 最終的に 50 が出力されます。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        int[] scores = {10, 50, 30, 20, 40};
        int maxVal = scores[0]; // Javaは0番目が先頭

        // 2番目の要素(index 1)から最後まで比較
        for (int i = 1; i < scores.length; i++) {
            if (scores[i] > maxVal) {
                maxVal = scores[i];
            }
        }
        System.out.println(maxVal);
    }
}


第8問:再帰関数(再帰呼び出し)

【問題】

次のプログラム中の関数 sigma(3) を呼び出したとき、戻り値はいくつか。

○整数型: sigma(整数型: n)
  if (n = 0)
    return 0
  endif
  return n + sigma(n - 1)


【解答】

6

【解説】

自分自身を呼び出す「再帰関数」の問題です 3。

sigma(n) は 1からnまでの和を求める関数として動作します。

  1. sigma(3) 呼び出し: 返り値は 3 + sigma(2)
  2. sigma(2) 呼び出し: 返り値は 2 + sigma(1)
  3. sigma(1) 呼び出し: 返り値は 1 + sigma(0)
  4. sigma(0) 呼び出し: 条件 n=0 に合致し、0 を返す。これらを積み上げると、3 + (2 + (1 + 0)) = 6 となります。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        System.out.println(sigma(3));
    }

    public static int sigma(int n) {
        if (n == 0) {
            return 0;
        }
        return n + sigma(n - 1);
    }
}


第9問:ネストされたループ(二重ループ)

【問題】

次のプログラムを実行したとき、出力される count の値はいくつか。

整数型: count ← 0
整数型: i, j

for (i を 1 から 3 まで 1 ずつ増やす)
  for (j を 1 から i まで 1 ずつ増やす)
    count ← count + 1
  endfor
endfor

count を出力

【解答】

6

【解説】

内側のループの回数が、外側の変数 i に依存して変化します。

  1. i = 1 のとき: 内側のループ j は 1 から 1 まで(1回)。count = 1。
  2. i = 2 のとき: 内側のループ j は 1 から 2 まで(2回)。count = 1 + 2 = 3。
  3. i = 3 のとき: 内側のループ j は 1 から 3 まで(3回)。count = 3 + 3 = 6。合計でループが6回回るため、答えは6です。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        int count = 0;
        for (int i = 1; i <= 3; i++) {
            for (int j = 1; j <= i; j++) {
                count = count + 1;
            }
        }
        System.out.println(count);
    }
}


第10問:配列の要素の入れ替え

【問題】

次のプログラムを実行したとき、出力される配列 data の内容はどれか。

※ div は整数の割り算の商(小数点以下切り捨て)を表すものとする。

整数型の配列: data ← {1, 2, 3, 4, 5}
整数型: n ← dataの要素数
整数型: i, temp

for (i を 1 から (n ÷ 2) の商 まで 1 ずつ増やす)
  /* 値の入れ替え */
  temp ← data[i]
  data[i] ← data[n - i + 1]
  data[n - i + 1] ← temp
endfor

data の全要素を出力

【解答】

{5, 4, 3, 2, 1}

【解説】

配列の順序を逆転(リバース)させるアルゴリズムです。

  • n は 5 なので、ループは 1 から 2 (5÷2の商) まで実行されます。
  1. i = 1 のとき: data[1] (1) と data[5 - 1 + 1] つまり data[5] (5) を交換します。
    • 配列は {5, 2, 3, 4, 1} になります。
  2. i = 2 のとき: data[2] (2) と data[5 - 2 + 1] つまり data[4] (4) を交換します。
    • 配列は {5, 4, 3, 2, 1} になります。
  3. ループ終了。真ん中の 3 は触られませんが、結果として配列全体が逆順になります。

【Javaコード】

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] data = {1, 2, 3, 4, 5};
        int n = data.length;
        int temp;

        // Javaの添字は0〜(n-1)。
        // 擬似言語の「iを1から n/2」は、Javaでは「iを0から n/2 - 1」まで等の調整が必要ですが、
        // ここではロジックを分かりやすくJava風に書き直します。
        // 左端(i)と右端(n-1-i)を交換します。
        for (int i = 0; i < n / 2; i++) {
            temp = data[i];
            data[i] = data[n - 1 - i];
            data[n - 1 - i] = temp;
        }
        System.out.println(Arrays.toString(data));
    }
}

第11問:2分探索(バイナリサーチ)

【問題】

次のプログラムは、昇順に整列された配列 data から、引数 target と等しい値を持つ要素を探し、その添字を返す関数 binarySearch である。

binarySearch({10, 20, 30, 40, 50, 60, 70}, 20) を呼び出したとき、変数 middle に代入される値の変化として正しいものはどれか。

※ ÷ は小数点以下を切り捨てる除算とする。

○整数型: binarySearch(整数型の配列: data, 整数型: target)
  整数型: low ← 1
  整数型: high ← dataの要素数
  整数型: middle

  while (low ≤ high)
    middle ← (low + high) ÷ 2
    if (data[middle] = target)
      return middle
    elseif (data[middle] < target)
      low ← middle + 1
    else
      high ← middle - 1
    endif
  endwhile
  return -1

【解答】

4 → 2

【解説】

探索範囲を半分に絞り込んでいく過程をトレースします。

  • 初期状態: low = 1, high = 7
  1. 1回目のループ:
    • middle(1 + 7) ÷ 2 = 4
    • data[4]40
    • 40 > 20 (target) なので、else 節へ。high4 - 1 = 3
  2. 2回目のループ:
    • low = 1, high = 3
    • middle(1 + 3) ÷ 2 = 2
    • data[2]20
    • 20 = 20 (target) なので、return middle (2) で終了。よって、middle の値は 4 → 2 と変化します。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        int[] data = {10, 20, 30, 40, 50, 60, 70};
        System.out.println(binarySearch(data, 20));
    }

    public static int binarySearch(int[] data, int target) {
        int low = 0; // Javaは0始まり
        int high = data.length - 1;
        int middle;

        while (low <= high) {
            middle = (low + high) / 2;
            System.out.println("middle: " + middle); // 変化を確認

            if (data[middle] == target) {
                return middle;
            } else if (data[middle] < target) {
                low = middle + 1;
            } else {
                high = middle - 1;
            }
        }
        return -1;
    }
}


第12問:文字列の圧縮(ランレングス符号化の基本)

【問題】

次のプログラムを実行したとき、出力される文字列は何か。

※演算子 + は文字列の連結を表す。

文字列型: s ← "AAABCCCC"
文字列型: res ← ""
整数型: count ← 1
整数型: i

for (i を 1 から sの文字数 - 1 まで 1 ずつ増やす)
  if (s の i 文字目 = s の (i + 1) 文字目)
    count ← count + 1
  else
    res ← res + s の i 文字目 + countを文字列にしたもの
    count ← 1
  endif
endfor
res ← res + s の 末尾の文字 + countを文字列にしたもの

res を出力

【解答】

"A3B1C4"

【解説】

連続する文字を「文字+個数」の形式に変換するアルゴリズムです。

  1. i=1 ('A'), i+1 ('A') → 一致。count=2。
  2. i=2 ('A'), i+1 ('A') → 一致。count=3。
  3. i=3 ('A'), i+1 ('B') → 不一致res に "A3" を追加。count=1 にリセット。
  4. i=4 ('B'), i+1 ('C') → 不一致res に "B1" を追加(現在は "A3B1")。count=1 にリセット。
  5. i=5 ('C'), i+1 ('C') → 一致。count=2。
  6. i=6 ('C'), i+1 ('C') → 一致。count=3。
  7. i=7 ('C'), i+1 ('C') → 一致。count=4。
  8. ループ終了後、最後の文字とカウントを追加。res に "C4" を追加。結果は "A3B1C4" となります。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        String s = "AAABCCCC";
        String res = "";
        int count = 1;

        for (int i = 0; i < s.length() - 1; i++) {
            if (s.charAt(i) == s.charAt(i + 1)) {
                count++;
            } else {
                res = res + s.charAt(i) + count;
                count = 1;
            }
        }
        // 最後の部分を追加
        res = res + s.charAt(s.length() - 1) + count;
        System.out.println(res);
    }
}


第13問:再帰関数(ユークリッドの互除法)

【問題】

次のプログラム中の関数 calc(24, 9) を呼び出したとき、戻り値はいくつか。

※ mod は剰余を表す。

○整数型: calc(整数型: a, 整数型: b)
  if (b = 0)
    return a
  endif
  return calc(b, a mod b)

【解答】

3

【解説】

これは最大公約数(GCD)を求める「ユークリッドの互除法」の実装です。

  1. calc(24, 9) 呼び出し。b (9) は 0 でない。
    • 24 mod 9 = 6
    • calc(9, 6) を呼び出す。
  2. calc(9, 6) 呼び出し。b (6) は 0 でない。
    • 9 mod 6 = 3
    • calc(6, 3) を呼び出す。
  3. calc(6, 3) 呼び出し。b (3) は 0 でない。
    • 6 mod 3 = 0
    • calc(3, 0) を呼び出す。
  4. calc(3, 0) 呼び出し。b は 0 である。
    • a (つまり 3) を返す。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        System.out.println(calc(24, 9));
    }

    public static int calc(int a, int b) {
        if (b == 0) {
            return a;
        }
        return calc(b, a % b);
    }
}


第14問:配列の操作(シフト処理)

【問題】

次のプログラムを実行したとき、出力される配列 arr の内容はどれか。

整数型の配列: arr ← {1, 2, 3, 4, 5}
整数型: temp
整数型: i

temp ← arr[1]
for (i を 1 から arrの要素数 - 1 まで 1 ずつ増やす)
  arr[i] ← arr[i + 1]
endfor
arr[arrの要素数] ← temp

arr を出力

【解答】

{2, 3, 4, 5, 1}

【解説】

配列の要素を「左に1つ回転シフト」させる処理です。

  1. 先頭の arr[1] (値は1) を temp に退避します。
  2. ループ処理で、i=1 から 4 まで実行します。
    • arr[1] ← arr[2] (2が入る)
    • arr[2] ← arr[3] (3が入る)
    • arr[3] ← arr[4] (4が入る)
    • arr[4] ← arr[5] (5が入る)
  3. 最後に、末尾 arr[5] に退避しておいた temp (1) を入れます。結果、{2, 3, 4, 5, 1} となります。

【Javaコード】

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        int temp = arr[0]; // 先頭を退避

        for (int i = 0; i < arr.length - 1; i++) {
            arr[i] = arr[i + 1];
        }
        arr[arr.length - 1] = temp; // 末尾に代入

        System.out.println(Arrays.toString(arr));
    }
}


第15問:スタック的な処理(LIFO)

【問題】

次のプログラムを実行したとき、出力される値はいくつか。

※関数 push は配列の末尾に要素を追加し、関数 pop は配列の末尾の要素を取り出して削除し、その値を返すものとする。

整数型の配列: stack ← {}
push(stack, 10)
push(stack, 20)
整数型: a ← pop(stack)
push(stack, 30)
push(stack, 40)
整数型: b ← pop(stack)
整数型: c ← pop(stack)

(a + b - c) を出力

【解答】

30

【解説】

スタック(後入れ先出し:LIFO)の動作を確認する問題です。

  1. stack: {10}
  2. stack: {10, 20}
  3. a ← pop(stack): 末尾の 20 を取り出す。a = 20stack: {10}
  4. stack: {10, 30}
  5. stack: {10, 30, 40}
  6. b ← pop(stack): 末尾の 40 を取り出す。b = 40stack: {10, 30}
  7. c ← pop(stack): 末尾の 30 を取り出す。c = 30stack: {10}
  8. 計算: a + b - c = 20 + 40 - 30 = 30

【Javaコード】

Javaでは java.util.Stack クラスなどを使用しますが、ここでは ArrayList を使って動作を模倣します。

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> stack = new ArrayList<>();
        
        stack.add(10); // push
        stack.add(20); // push
        
        int a = stack.remove(stack.size() - 1); // pop
        
        stack.add(30); // push
        stack.add(40); // push
        
        int b = stack.remove(stack.size() - 1); // pop
        int c = stack.remove(stack.size() - 1); // pop
        
        System.out.println(a + b - c);
    }
}


第16問:単方向リストの操作

【問題】

次のプログラムは、単方向リストの先頭から2番目の要素を削除する処理である。

リストの構造は [Head] -> [A] -> [B] -> [C] となっており、Head.next は要素Aを指している。

プログラム実行後のリストの並びとして正しいものはどれか。

クラス ListElement
  メンバ変数 val: 文字列型
  メンバ変数 next: ListElement型
  
○手続名: deleteSecond(ListElement: head)
  ListElement: p1
  ListElement: p2
  
  /* p1は1番目の要素(A)を指す */
  p1 ← head.next
  
  /* p2は2番目の要素(B)を指す */
  p2 ← p1.next
  
  /* リンクのつなぎ替え */
  p1.next ← p2.next

【解答】

[Head] -> [A] -> [C]

【解説】

単方向リストの削除操作のトレースです。

  1. 初期状態: Head -> A -> B -> C
  2. p1 ← head.next: p1A を指します。
  3. p2 ← p1.next: p1(A) の次は B なので、p2B を指します。
  4. p1.next ← p2.next:
    • p2(B) の nextC です。
    • p1(A) の nextC に書き換えます。
  5. 結果、A の次は直接 C を指すようになり、B はリストから外れます。
    • Head -> A -> C

【Javaコード】

class ListElement {
    String val;
    ListElement next;
    ListElement(String val) { this.val = val; }
}

public class Main {
    public static void main(String[] args) {
        // リストの構築: Head -> A -> B -> C
        ListElement head = new ListElement("Head");
        ListElement a = new ListElement("A");
        ListElement b = new ListElement("B");
        ListElement c = new ListElement("C");
        
        head.next = a;
        a.next = b;
        b.next = c;
        
        // 削除処理
        ListElement p1 = head.next; // A
        ListElement p2 = p1.next;   // B
        p1.next = p2.next;          // A -> C
        
        // 結果確認
        System.out.println(head.next.val + " -> " + head.next.next.val);
    }
}


第17問:2分木の巡回(再帰)

【問題】

次のプログラムは、2分木を巡回してノードの値を出力する関数 traverse である。

下図の構造を持つ木に対して traverse(Node1) を実行したとき、出力される順番はどれか。

※ nil は子が何もないことを表す。

[木の構造]

    1
   / \
  2   3
 /
4

クラス Node
  メンバ変数 val: 整数型
  メンバ変数 left: Node型
  メンバ変数 right: Node型

○手続名: traverse(Node: n)
  if (n = nil)
    return
  endif
  traverse(n.left)
  n.val を出力
  traverse(n.right)

【解答】

4, 2, 1, 3

【解説】

これは「通りがけ順(In-order)」の巡回アルゴリズムです。

処理順序:左の部分木 → 自分 → 右の部分木

  1. traverse(1) 開始
    • traverse(1.left) つまり traverse(2) を呼び出し。
      • traverse(2.left) つまり traverse(4) を呼び出し。
        • traverse(4.left) は nil なのでリターン。
        • 4 を出力
        • traverse(4.right) は nil なのでリターン。
      • traverse(4) 終了。
      • 2 を出力
      • traverse(2.right) は nil なのでリターン。
    • traverse(2) 終了。
    • 1 を出力
    • traverse(1.right) つまり traverse(3) を呼び出し。
      • traverse(3.left) は nil なのでリターン。
      • 3 を出力
      • traverse(3.right) は nil なのでリターン。
  2. 出力順序は 4 → 2 → 1 → 3 となります。

【Javaコード】

class Node {
    int val;
    Node left, right;
    Node(int val) { this.val = val; }
}

public class Main {
    public static void main(String[] args) {
        Node n1 = new Node(1);
        Node n2 = new Node(2);
        Node n3 = new Node(3);
        Node n4 = new Node(4);
        
        n1.left = n2; n1.right = n3;
        n2.left = n4;
        
        traverse(n1);
    }

    static void traverse(Node n) {
        if (n == null) return;
        traverse(n.left);
        System.out.println(n.val);
        traverse(n.right);
    }
}

第18問:ビット演算(論理積とシフト)

【問題】

次のプログラム中の関数 getBit(20, 3) を呼び出したとき、戻り値はいくつか。

※整数 20 の2進数表現は 10100 である。

※ << は左シフト、and はビットごとの論理積を表す。

○整数型: getBit(整数型: val, 整数型: n)
  整数型: mask ← 1
  /* 1 を (n-1) ビット左にシフトする */
  mask ← mask << (n - 1)
  
  if ((val and mask) ≠ 0)
    return 1
  else
    return 0
  endif

【解答】

1

【解説】

指定されたビット位置(下位からn番目)が 1 か 0 かを判定する処理です。

引数: val = 20 (10100), n = 3

  1. mask の初期値は 1 (00001)。
  2. mask3 - 1 = 2 ビット左シフトします。
    • 00001 << 2 = 00100 (10進数で4)
  3. valmask の論理積 (and) をとります。
    • 10100 (20)
    • 00100 (4)
    • and ↓
    • 00100 (4)
  4. 結果は 4 であり、0 ではないため、if 文が真となり 1 を返します。これは、下位から3番目のビットが立っていることを意味します。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        System.out.println(getBit(20, 3));
    }

    static int getBit(int val, int n) {
        int mask = 1;
        mask = mask << (n - 1);
        
        if ((val & mask) != 0) {
            return 1;
        } else {
            return 0;
        }
    }
}


第19問:クラスとインスタンス(状態管理)

【問題】

次のプログラムを実行したとき、出力される値はいくつか。

クラス Counter
  メンバ変数 count: 整数型
  
  ○Counter()  /* コンストラクタ */
    count ← 0
    
  ○add(整数型: n)
    count ← count + n
    
  ○clear()
    count ← 0

/* メイン処理 */
Counter: c1 ← Counter()
Counter: c2 ← Counter()

c1.add(5)
c2.add(10)
c1.add(3)
c2.clear()
c2.add(2)

(c1.count + c2.count) を出力

【解答】

10

【解説】

オブジェクト指向における「インスタンスごとの独立性」を問う問題です。

c1 と c2 は別のメモリ領域を持つため、互いの count は干渉しません。

  1. c1.add(5): c1.count は 5。
  2. c2.add(10): c2.count は 10。
  3. c1.add(3): c1.count は 5 + 3 = 8
  4. c2.clear(): c2.count0
  5. c2.add(2): c2.count は 0 + 2 = 2
  6. 出力: c1.count (8) + c2.count (2) = 10

【Javaコード】

class Counter {
    int count;
    Counter() { count = 0; }
    void add(int n) { count += n; }
    void clear() { count = 0; }
}

public class Main {
    public static void main(String[] args) {
        Counter c1 = new Counter();
        Counter c2 = new Counter();
        
        c1.add(5);
        c2.add(10);
        c1.add(3);
        c2.clear();
        c2.add(2);
        
        System.out.println(c1.count + c2.count);
    }
}


第20問:選択ソート(アルゴリズムの穴埋め)

【問題】

次のプログラムは、配列 data を昇順に並べ替える(ソートする)ものである。

プログラムを実行して正しくソートするために、 [ 空欄 ] に入れるべき適切な式はどれか。

整数型の配列: data ← {4, 3, 1, 5, 2}
整数型: i, j, minIndex, temp

for (i を 1 から dataの要素数 - 1 まで 1 ずつ増やす)
  minIndex ← i
  
  /* 未ソート部分から最小値の位置を探す */
  for (j を i + 1 から dataの要素数 まで 1 ずつ増やす)
    if ( [ 空欄 ] )
      minIndex ← j
    endif
  endfor
  
  /* 値の交換 */
  temp ← data[i]
  data[i] ← data[minIndex]
  data[minIndex] ← temp
endfor

【解答】

data[j] < data[minIndex]

【解説】

選択ソート(Selection Sort)のアルゴリズムです。

  1. 外側のループ i は、ソート済み位置の境界を決めます。
  2. 内側のループ j で、i 以降の範囲(未ソート部分)から「最小値」を探します。
  3. 現在の暫定的な最小値 data[minIndex] よりも、さらに小さい値 data[j] が見つかった場合、minIndexj に更新する必要があります。
  4. したがって、条件式は「新しい値の方が小さいか?」を問う data[j] < data[minIndex] となります。

【Javaコード】

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] data = {4, 3, 1, 5, 2};
        int minIndex, temp;
        
        for (int i = 0; i < data.length - 1; i++) {
            minIndex = i;
            for (int j = i + 1; j < data.length; j++) {
                // Javaは0始まりですがロジックは同じ
                if (data[j] < data[minIndex]) {
                    minIndex = j;
                }
            }
            // 交換
            temp = data[i];
            data[i] = data[minIndex];
            data[minIndex] = temp;
        }
        System.out.println(Arrays.toString(data));
    }
}


第21問:ハッシュ法(オープンアドレス法)

【問題】

次のプログラムは、ハッシュ関数 h(x) = x mod 10 を用いてデータを配列 table に格納するものである。

衝突(コリジョン)が発生した場合は、格納位置を 1 つ後ろにずらす「オープンアドレス法(線形探索法)」を用いる。

データ 15, 25, 35 を順に格納した後、データ 35 が格納されている配列の添字(インデックス)はいくつか。

※配列の初期値はすべて -1(空)とする。

整数型の配列: table ← {-1, -1, -1, ...} (要素数10)
整数型の配列: data ← {15, 25, 35}
整数型: i, pos

for (i を 1 から 3 まで 1 ずつ増やす)
    pos ← data[i] mod 10
    /* 空きが見つかるまで位置をずらす */
    while (table[pos] ≠ -1)
        pos ← (pos + 1) mod 10
    endwhile
    table[pos] ← data[i]
endfor
35 が格納された pos を出力

【解答】

7

【解説】

ハッシュ値の衝突処理をトレースする問題です。ハッシュ値はすべて x mod 10 で計算されます。

  1. 15 の格納:
    • ハッシュ値: 15 mod 10 = 5
    • table[5] は空なので、そこに 15 を格納します。
  2. 25 の格納:
    • ハッシュ値: 25 mod 10 = 5
    • table[5] には既に 15 があるため衝突します。
    • pos を 1 ずらして 6 にします。table[6] は空なので、そこに 25 を格納します。
  3. 35 の格納:
    • ハッシュ値: 35 mod 10 = 5
    • table[5] (15) → 埋まっている。次は 6。
    • table[6] (25) → 埋まっている。次は 7。
    • table[7] は空なので、そこに 35 を格納します。
    • 結果、7 が出力されます。

【Javaコード】

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] table = new int[10];
        Arrays.fill(table, -1);
        int[] data = {15, 25, 35};
        int finalPos = -1;

        for (int x : data) {
            int pos = x % 10;
            while (table[pos] != -1) {
                pos = (pos + 1) % 10;
            }
            table[pos] = x;
            if (x == 35) finalPos = pos;
        }
        System.out.println(finalPos);
    }
}


第22問:マージソート(併合処理)

【問題】

次のプログラムは、昇順に整列済みの2つの配列 arrA と arrB を、昇順を保ったまま1つの配列 arrC にまとめる(マージする)処理である。

プログラム実行直後、arrC の 4番目 の要素に格納されている値はいくつか。

整数型の配列: arrA ← {2, 5, 9}
整数型の配列: arrB ← {1, 6, 8}
整数型の配列: arrC ← {} (要素数6の空配列)
整数型: a ← 1, b ← 1, c ← 1

while (a ≤ 3 and b ≤ 3)
    if (arrA[a] < arrB[b])
        arrC[c] ← arrA[a]
        a ← a + 1
    else
        arrC[c] ← arrB[b]
        b ← b + 1
    endif
    c ← c + 1
endwhile
/* 残りの要素をコピーする処理は省略 */
arrC[4] を出力

【解答】

6

【解説】

2つの配列の先頭同士を比較し、小さい方を採用していく処理です。

  1. 1回目: A:2 vs B:11 (B) を採用。 arrC={1}, bが進む。
  2. 2回目: A:2 vs B:62 (A) を採用。 arrC={1, 2}, aが進む。
  3. 3回目: A:5 vs B:65 (A) を採用。 arrC={1, 2, 5}, aが進む。
  4. 4回目:A:9 vs B:66 (B) を採用。 arrC={1, 2, 5, 6}, bが進む。
    • ここで arrC の4番目の要素が決まりました。値は 6 です。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        int[] arrA = {2, 5, 9};
        int[] arrB = {1, 6, 8};
        int[] arrC = new int[6];
        int a = 0, b = 0, c = 0;

        // 擬似言語の1始まりを0始まりに読み替え
        while (a < arrA.length && b < arrB.length) {
            if (arrA[a] < arrB[b]) {
                arrC[c++] = arrA[a++];
            } else {
                arrC[c++] = arrB[b++];
            }
        }
        // Java配列は0から始まるため、4番目はインデックス3
        System.out.println(arrC[3]); 
    }
}


第23問:ビット操作と論理演算(XOR)

【問題】

次のプログラムを実行したとき、出力される値はいくつか。

※ xor は排他的論理和を表し、2進数でビットごとに演算を行う。

整数型: a ← 10
整数型: b ← 6
整数型: temp

/* 値の交換(XORスワップアルゴリズムの一部) */
temp ← a xor b
a ← temp
temp ← a xor b
/* ここでの temp の値を出力 */
temp を出力

【解答】

10

【解説】

ビット演算の性質を問う問題です。

10進数の10は2進数で 1010、6は 0110 です。

  1. 最初の temp:
    • 1010 (10) xor 0110 (6) = 1100 (12)。
    • a に 12 が代入されます。
  2. 次の temp:
    • 現在の a は 12 (1100)。
    • b は変わらず 6 (0110)。
    • 1100 (12) xor 0110 (6) = 1010 (10)。
    • 結果として、元の a の値である 10 が出力されます。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        int a = 10; // 1010
        int b = 6;  // 0110
        
        int temp = a ^ b; // 1100 (12)
        a = temp;
        
        temp = a ^ b; // 1100 ^ 0110 = 1010 (10)
        
        System.out.println(temp);
    }
}


第24問:無向グラフの隣接行列

【問題】

次の2次元配列 adj は、ノード1から3までの無向グラフの接続関係を表している(1は接続あり、0は接続なし)。

ノード2に接続されている辺の本数(次数)はいくつか。

整数型の配列: adj ← {
  {0, 1, 1}, 
  {1, 0, 1}, 
  {1, 1, 0} 
}
整数型: count ← 0
整数型: i

/* ノード2(配列の2行目)の接続数をカウント */
for (i を 1 から 3 まで 1 ずつ増やす)
    if (adj[2][i] = 1)
        count ← count + 1
    endif
endfor
count を出力

【解答】

2

【解説】

隣接行列における次数(つながっている線の数)の計算問題です。

ノード2に対応するのは配列の2行目(擬似言語では添字2、Java等では添字1)です。

  • 配列 adj の2行目は {1, 0, 1} です。
  • i=1: adj[2][1] は 1。(カウント+1)
  • i=2: adj[2][2] は 0。(自分自身へのループなし)
  • i=3: adj[2][3] は 1。(カウント+1)
  • 合計で 2 となります。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        int[][] adj = {
            {0, 1, 1},
            {1, 0, 1},
            {1, 1, 0}
        };
        int count = 0;
        // ノード2に対応するのはインデックス1
        for (int i = 0; i < 3; i++) {
            if (adj[1][i] == 1) {
                count++;
            }
        }
        System.out.println(count);
    }
}


第25問:双方向リスト(ノードの削除)

【問題】

次のプログラムは、配列を用いて実装された双方向リストから、要素 20 を削除する処理である。

処理終了後、要素 10 の「次の要素」を指す next 配列の値はいくつになっているか。

prev は前の要素の添字、next は次の要素の添字を表す。

/* インデックス:    1   2   3  */
/* 値(data):      10, 20, 30 */
整数型の配列: next ← {2,  3, -1}
整数型の配列: prev ← {-1, 1,  2}

整数型: deleteIdx ← 2  /* 削除する要素(20)の添字 */
整数型: p ← prev[deleteIdx] /* 1 (10の場所) */
整数型: n ← next[deleteIdx] /* 3 (30の場所) */

/* 削除処理:ポインタの付け替え */
if (p ≠ -1)
    next[p] ← n
endif
if (n ≠ -1)
    prev[n] ← p
endif

next[1] を出力

【解答】

3

【解説】

双方向リストの削除操作では、削除するノードの前後のノードを直接つなぎ合わせます。

  1. 初期状態:
    • ノード1(10)の次は ノード2(20)。 (next[1] = 2)
    • ノード2(20)の次は ノード3(30)。
    • ノード3(30)の前は ノード2(20)。
  2. 削除対象:deleteIdx = 2 (値20)。
    • 前のノード p は 1。
    • 次のノード n は 3。
  3. 付け替え:
    • next[p] ← n : 「前のノード(1)の次」を、「次のノード(3)」に書き換えます。
    • つまり next[1]3 になります。
    • prev[n] ← p : 「次のノード(3)の前」を、「前のノード(1)」に書き換えます。
  4. 結果:
    • next[1] の値は 3 です。これでリストは 10 → 30 と繋がりました。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        // インデックス0は使わず、1,2,3を使用すると仮定
        int[] next = {0, 2, 3, -1};
        int[] prev = {0, -1, 1, 2};
        
        int deleteIdx = 2;
        int p = prev[deleteIdx]; // 1
        int n = next[deleteIdx]; // 3
        
        if (p != -1) next[p] = n;
        if (n != -1) prev[n] = p;
        
        System.out.println(next[1]);
    }
}

第26問:バブルソート(隣接交換法)

【問題】

次のプログラムは、配列を昇順に整列するバブルソートである。

外側のループ(変数 i)が 1回終了した時点 で、配列 nums の内容はどのように変化しているか。

整数型の配列: nums ← {4, 2, 5, 1, 3}
整数型: i, j, temp
/* 末尾から先頭に向かって順に比較・交換を行う */
for (i を 1 から numsの要素数 - 1 まで 1 ずつ増やす)
    for (j を numsの要素数 から i + 1 まで 1 ずつ減らす)
        if (nums[j] < nums[j - 1])
            /* 交換処理 */
            temp ← nums[j]
            nums[j] ← nums[j - 1]
            nums[j - 1] ← temp
        endif
    endfor
endfor
nums を出力

【解答】

{1, 4, 2, 5, 3}

【解説】

バブルソートのこの実装は、後ろから前へ向かって小さい値を「泡のように」浮かび上がらせる方式です。

  1. i = 1 のループ(先頭 nums[1] を確定させるパス)
    • j = 5: nums[5] (3) と nums[4] (1) を比較。3 > 1 なので交換しない。
    • j = 4: nums[4] (1) と nums[3] (5) を比較。1 < 5 なので交換。配列は {4, 2, 1, 5, 3}
    • j = 3: nums[3] (1) と nums[2] (2) を比較。1 < 2 なので交換。配列は {4, 1, 2, 5, 3}
    • j = 2: nums[2] (1) と nums[1] (4) を比較。1 < 4 なので交換。配列は {1, 4, 2, 5, 3}
  2. 結果
    • 最も小さい値 1 が先頭に移動し、残りの並びは交換過程の結果に従います。

【Javaコード】

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] nums = {4, 2, 5, 1, 3};
        // 外側のループ1回分 (i=0に対応)
        for (int j = nums.length - 1; j > 0; j--) {
            if (nums[j] < nums[j - 1]) {
                int temp = nums[j];
                nums[j] = nums[j - 1];
                nums[j - 1] = temp;
            }
        }
        System.out.println(Arrays.toString(nums));
    }
}


第27問:挿入ソート

【問題】

次のプログラムは、配列を昇順に整列する挿入ソートの途中経過である。

変数 i が 3 のときのループ処理が終了した直後、配列 cards の内容はどれか。

整数型の配列: cards ← {10, 5, 8, 3, 6}
整数型: i, j, temp

/* 2番目の要素から順に、適切な位置に挿入していく */
for (i を 2 から cardsの要素数 まで 1 ずつ増やす)
    temp ← cards[i]
    j ← i - 1
    while (j ≥ 1 and cards[j] > temp)
        cards[j + 1] ← cards[j]
        j ← j - 1
    endwhile
    cards[j + 1] ← temp
endfor

【解答】

{5, 8, 10, 3, 6}

【解説】

挿入ソートは、左側の「整列済み部分」に対して、新しい要素を適切な場所に差し込むアルゴリズムです。

  1. i = 2 のとき(値 5 の処理):
    • 10 と 5 を比較し、5 を先頭に挿入。
    • 配列は {5, 10, 8, 3, 6} となる。
  2. i = 3 のとき(値 8 の処理):
    • 整列済み部分 {5, 10} に対して、8 を適切な場所に挿入します。
    • 10 > 8 なので、10 を右にずらす。
    • 5 < 8 なので、5 の後ろ(10があった場所)に 8 を入れる。
    • 配列は {5, 8, 10, 3, 6} となる。
    • ※後ろの 3, 6 はまだ手付かずです。

【Javaコード】

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] cards = {10, 5, 8, 3, 6};
        // Javaでは i=1 が擬似言語の i=2 に相当
        for (int i = 1; i <= 2; i++) { // i=2(3番目の要素)まで実行
            int temp = cards[i];
            int j = i - 1;
            while (j >= 0 && cards[j] > temp) {
                cards[j + 1] = cards[j];
                j--;
            }
            cards[j + 1] = temp;
        }
        System.out.println(Arrays.toString(cards));
    }
}

第28問:パリティビットと誤り検出

【問題】

次のプログラムは、7ビットのデータに「偶数パリティビット」を付加して8ビットにする処理である。

入力データ data が 1011001 のとき、出力される parity の値(0 または 1)はいくつか。

※偶数パリティとは、データとパリティビットを合わせた「1」の個数が偶数になるようにする方式である。

/* data は整数の配列として各ビットを表す {1, 0, 1, 1, 0, 0, 1} */
整数型の配列: data ← {1, 0, 1, 1, 0, 0, 1}
整数型: sum ← 0
整数型: k, parity

for (k を 1 から 7 まで 1 ずつ増やす)
    sum ← sum + data[k]
endfor

if (sum mod 2 = 0)
    parity ← 0
else
    parity ← 1
endif

parity を出力

【解答】

0

【解説】

偶数パリティでは、「1の総数」が偶数ならパリティビットは0、奇数なら1になります(そうすることで全体の1の数を偶数にするため)。

  1. 配列 data の要素を合計します。
    • 1 + 0 + 1 + 1 + 0 + 0 + 1 = 4
  2. 合計 sum は 4 です。
  3. 4 mod 2 は 0(偶数)です。
  4. 条件分岐 if (sum mod 2 = 0) が真となるため、parity には 0 が代入されます。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        int[] data = {1, 0, 1, 1, 0, 0, 1};
        int sum = 0;
        for (int bit : data) {
            sum += bit;
        }
        int parity;
        if (sum % 2 == 0) {
            parity = 0;
        } else {
            parity = 1;
        }
        System.out.println(parity);
    }
}


第29問:グラフ走査(深さ優先探索・再帰)

【問題】

次のプログラムは、ノード1からスタートして、接続されているノードを「深さ優先探索 (DFS)」で順に訪問するものである。

visit(1) を呼び出したとき、出力される数値の順序はどれか。

※ adj は隣接リストを表し、adj[1] はノード1から行けるノードのリストである。

配列の配列: adj ← {
    {},        /* index 0: 未使用 */
    {2, 3},    /* index 1: 1から2と3へ */
    {4},       /* index 2: 2から4へ */
    {},        /* index 3: 行き先なし */
    {}         /* index 4: 行き先なし */
}

○visit(整数型: n)
    n を出力
    整数型: i
    for (i を 1 から adj[n]の要素数 まで 1 ずつ増やす)
        visit(adj[n][i])
    endfor

【解答】

1 → 2 → 4 → 3

【解説】

深さ優先探索(DFS)は、行けるところまで奥へ進み、行き止まりになったら戻る(バックトラック)アルゴリズムです。

  1. visit(1) 呼び出し。 1 を出力。
    • 隣接リスト adj[1]{2, 3}。まずは先頭の 2 へ進む。
  2. visit(2) 呼び出し。 2 を出力。
    • 隣接リスト adj[2]{4}4 へ進む。
  3. visit(4) 呼び出し。 4 を出力。
    • 隣接リスト adj[4] は空。関数終了し、visit(2) へ戻る。
    • visit(2) もこれ以上行く場所がないので終了し、visit(1) へ戻る。
  4. visit(1) の続き
    • さきほどは 2 へ行ったので、次は 3 へ進む。
  5. visit(3) 呼び出し。 3 を出力。
    • 隣接リスト adj[3] は空。関数終了。

結果、順序は 1, 2, 4, 3 となります。


【Javaコード】

public class Main {
    static int[][] adj = {
        {},
        {2, 3},
        {4},
        {},
        {}
    };

    public static void main(String[] args) {
        visit(1);
    }

    public static void visit(int n) {
        System.out.println(n);
        for (int neighbor : adj[n]) {
            visit(neighbor);
        }
    }
}

第30問:状態遷移(オートマトン)

【問題】

次のプログラムは、入力された数値に応じて状態(state)を変化させる処理である。

配列 inputs {1, 0, 1, 1} を順に処理した後、最終的な state の値はいくつか。

※ transition[現在の状態][入力値] で次の状態が決まるものとする。

/* 状態遷移表: transition[state][input] */
整数型の配列: transition ← {
  {0, 1},  /* state 0 のとき: 入力0なら0, 入力1なら1へ */
  {2, 1},  /* state 1 のとき: 入力0なら2, 入力1なら1へ */
  {0, 2}   /* state 2 のとき: 入力0なら0, 入力1なら2へ */
}
整数型の配列: inputs ← {1, 0, 1, 1}
整数型: state ← 0
整数型: k

for (k を 1 から inputsの要素数 まで 1 ずつ増やす)
    state ← transition[state][inputs[k] + 1] 
    /* ※擬似言語の配列添字は1始まりのため、input(0/1)に+1して列を指定 */
endfor
state を出力

【解答】

2

【解説】

表に従って状態を遷移させます。

  1. 初期状態: state = 0
  2. 1つ目の入力 (1): transition[0][1] (※入力1に対応する列) を参照 → 次の状態は 1
  3. 2つ目の入力 (0): state = 1transition[1][0] を参照 → 次の状態は 2
  4. 3つ目の入力 (1): state = 2transition[2][1] を参照 → 次の状態は 2
  5. 4つ目の入力 (1): state = 2transition[2][1] を参照 → 次の状態は 2
  6. 最終的な状態は 2 となります。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        // Javaは0始まりなのでそのまま使用
        int[][] transition = {
            {0, 1}, // state 0
            {2, 1}, // state 1
            {0, 2}  // state 2
        };
        int[] inputs = {1, 0, 1, 1};
        int state = 0;

        for (int input : inputs) {
            state = transition[state][input];
        }
        System.out.println(state);
    }
}


第31問:クイックソート(パーティション分割)

【問題】

次のプログラムは、クイックソートの一部である「パーティション分割」を行う関数 partition である。

配列 data の最後の要素を「基準値(ピボット)」とし、それより小さい値を左側、大きい値を右側に集める処理を行う。

プログラム実行直後、配列 data の内容はどのように変化しているか。

整数型の配列: data ← {2, 6, 1, 7, 4}
整数型: pivot ← data[dataの要素数]  /* ピボットは最後の 4 */
整数型: i ← 0  /* 小さい要素の境界線 */
整数型: j, temp

/* 1番目から (要素数-1) 番目まで走査 */
for (j を 1 から dataの要素数 - 1 まで 1 ずつ増やす)
    if (data[j] < pivot)
        i ← i + 1
        /* data[i] と data[j] を交換 */
        temp ← data[i]
        data[i] ← data[j]
        data[j] ← temp
    endif
endfor

/* 最後にピボットを境界線の隣に移動 */
temp ← data[i + 1]
data[i + 1] ← data[dataの要素数]
data[dataの要素数] ← temp

data を出力

【解答】

{2, 1, 4, 7, 6}

【解説】

ピボット(4)より小さい値を配列の前方に詰め込み、最後にピボットをその境界に置く処理です。

  1. 初期状態: i=0, pivot=4, 配列 {2, 6, 1, 7, 4}
  2. j=1: 2 < 4 なので、iを1増やして i=1data[1](2) と data[1](2) を交換(変化なし)。
  3. j=2: 6 > 4 なので何もしない。
  4. j=3: 1 < 4 なので、iを1増やして i=2data[2](6) と data[3](1) を交換。配列は {2, 1, 6, 7, 4}
  5. j=4: 7 > 4 なので何もしない。
  6. ループ終了後: data[i+1] つまり data[3](6) と、末尾のピボット(4)を交換。配列は {2, 1, 4, 7, 6} となります。これで、4より左は小さく、右は大きくなりました。

【Javaコード】

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] data = {2, 6, 1, 7, 4};
        int pivot = data[data.length - 1]; // pivot = 4
        int i = -1; // Javaは0始まりなので、擬似言語の0に対応するのは-1

        for (int j = 0; j < data.length - 1; j++) {
            if (data[j] < pivot) {
                i++;
                int temp = data[i];
                data[i] = data[j];
                data[j] = temp;
            }
        }
        // ピボットの移動
        int temp = data[i + 1];
        data[i + 1] = data[data.length - 1];
        data[data.length - 1] = temp;

        System.out.println(Arrays.toString(data));
    }
}


第32問:ゲーム木の探索(ミニマックス法)

【問題】

次のプログラムは、ゲーム木のスコアを計算する再帰関数 minimax である。

minimax(0, true) を呼び出したとき、戻り値はいくつか。

※引数 isMax が true のときは自分の手番(最大値を選ぶ)、false のときは相手の手番(最小値を選ぶ)を表す。

/* 木構造のデータ(葉のスコア)を配列で表現 */
/* index 0 は根。1,2 はその子。3,4,5,6 は葉 */
整数型の配列: scores ← {0, 0, 0, 5, 3, 8, 1}

○整数型: minimax(整数型: node, ブール型: isMax)
    /* 葉ノード(これ以上子がない)なら値を返す */
    if (node ≥ 3)
        return scores[node]
    endif

    if (isMax = true)
        /* 自分: 子ノード(左:2*node+1, 右:2*node+2)のうち大きい方を選ぶ */
        return max(minimax(2 * node + 1, false), minimax(2 * node + 2, false))
    else
        /* 相手: 小さい方を選ぶ */
        return min(minimax(2 * node + 1, true), minimax(2 * node + 2, true))
    endif

【解答】

3

【解説】

自分はスコアを最大化、相手は最小化するように動くと仮定して、根(ルート)の値を求める問題です。

  1. 葉の値: ノード3=5, ノード4=3, ノード5=8, ノード6=1
  2. ノード1(相手の番: node 3, 4 から最小を選択):
    • min(5, 3) = 3
  3. ノード2(相手の番: node 5, 6 から最小を選択):
    • min(8, 1) = 1
  4. ノード0(自分の番: node 1, 2 から最大を選択):
    • max(3, 1) = 3
    • したがって、戻り値は 3 です。

【Javaコード】

public class Main {
    static int[] scores = {0, 0, 0, 5, 3, 8, 1}; // index 0~6

    public static void main(String[] args) {
        System.out.println(minimax(0, true));
    }

    public static int minimax(int node, boolean isMax) {
        // 簡易的な判定:配列外なら終了とみなす等の処理が必要だが
        // ここでは問題文の通りnode>=3で葉とする
        if (node >= 3) {
            return scores[node];
        }

        if (isMax) {
            int left = minimax(2 * node + 1, false);
            int right = minimax(2 * node + 2, false);
            return Math.max(left, right);
        } else {
            int left = minimax(2 * node + 1, true);
            int right = minimax(2 * node + 2, true);
            return Math.min(left, right);
        }
    }
}


第33問:ナップサック問題(動的計画法)

【問題】

次のプログラムは、重さの上限(capacity)まで荷物を入れたときの価値の最大値を求めるものである。

プログラム実行後、出力される dp[5] の値はいくつか。

※ dp[w] は、重さ w まで入るリュックの最大価値を表す。

整数型の配列: weights ← {2, 3}  /* 荷物の重さ */
整数型の配列: values  ← {3, 4}  /* 荷物の価値 */
整数型の配列: dp ← {0, 0, 0, 0, 0, 0} /* 重さ0〜5に対応 */
整数型: capacity ← 5
整数型: i, w

/* 荷物の種類ごとにDPテーブルを更新 */
for (i を 1 から 2 まで 1 ずつ増やす)
    /* 重い方から更新することで、同じ荷物の重複利用を防ぐ(0/1ナップサック) */
    for (w を capacity から weights[i] まで 1 ずつ減らす)
        if (dp[w] < dp[w - weights[i]] + values[i])
            dp[w] ← dp[w - weights[i]] + values[i]
        endif
    endfor
endfor
dp[5] を出力

【解答】

7

【解説】

動的計画法(DP)を用いて、価値の最大値を積み上げていく問題です。

  1. 初期状態: dp はすべて 0。
  2. 荷物1 (重さ2, 価値3) の処理:
    • w=5: dp[5] vs dp[3]+30 vs 33
    • w=4: dp[4] vs dp[2]+33
    • w=3: dp[3] vs dp[1]+33
    • w=2: dp[2] vs dp[0]+33
    • 配列状態: {0, 0, 3, 3, 3, 3}
  3. 荷物2 (重さ3, 価値4) の処理:
    • w=5: dp[5](3) vs dp[5-3](3) + 4 → 3 vs 77 に更新。
    • w=4: dp[4](3) vs dp[4-3](0) + 4 → 3 vs 44 に更新。
    • w=3: dp[3](3) vs dp[3-3](0) + 4 → 3 vs 44 に更新。
    • 配列状態: {0, 0, 3, 4, 4, 7}
  4. dp[5] の値である 7 が出力されます。

【Javaコード】

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] weights = {2, 3};
        int[] values = {3, 4};
        int capacity = 5;
        int[] dp = new int[capacity + 1]; // 0~5

        for (int i = 0; i < weights.length; i++) {
            int weight = weights[i];
            int value = values[i];
            // 重複なし(0/1)のため、後ろからループ
            for (int w = capacity; w >= weight; w--) {
                if (dp[w] < dp[w - weight] + value) {
                    dp[w] = dp[w - weight] + value;
                }
            }
        }
        System.out.println(dp[5]);
    }
}


第34問:多項式の計算(ホーナー法)

【問題】

次のプログラムは、多項式P(x) = 3x^3 + 2x^2 + x + 5 の値を、x=2 のときについて計算するものである。

このアルゴリズム(ホーナー法)を用いたとき、出力される result の値はいくつか。

整数型の配列: coef ← {3, 2, 1, 5} /* 係数: 3x^3 + 2x^2 + 1x + 5 */
整数型: x ← 2
整数型: result ← coef[1] /* 最上位の係数(3)で初期化 */
整数型: i

for (i を 2 から 4 まで 1 ずつ増やす)
    result ← result × x + coef[i]
endfor
result を出力

【解答】

39

【解説】

ホーナー法は、多項式を ((\dots(a_n x + a_{n-1})x + \dots)x + a_0 の形に変形して計算する方法です。乗算の回数を減らせます。

  1. 初期化: result = 3
  2. i=2 (係数2):
    • result = 3 * 2 + 2 = 8
    • (現在の式: 3x + 2
  3. i=3 (係数1):
    • result = 8 * 2 + 1 = 17
    • (現在の式: 3x^2 + 2x + 1
  4. i=4 (係数5):
    • result = 17 * 2 + 5 = 34 + 5 = 39
    • (現在の式: 3x^3 + 2x^2 + x + 5
  5. 結果 39 が出力されます。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        int[] coef = {3, 2, 1, 5};
        int x = 2;
        int result = coef[0]; // 最上位の係数
        
        for (int i = 1; i < coef.length; i++) {
            result = result * x + coef[i];
        }
        System.out.println(result);
    }
}


第35問:繰り返し二乗法(べき乗の剰余)

【問題】

次のプログラムは、べき乗 base^{exp} \pmod{mod} を高速に計算する「繰り返し二乗法」のアルゴリズムである。

2^{10} \pmod{1000} を計算したとき、出力される ans の値はいくつか。

※演算子 mod は剰余、div は整数の商を表す。

整数型: base ← 2
整数型: exp ← 10   /* 2進数で 1010 */
整数型: mod ← 1000
整数型: ans ← 1

while (exp > 0)
    /* 指数の最下位ビットが 1 なら現在の base を掛ける */
    if (exp mod 2 = 1)
        ans ← (ans × base) mod mod
    endif
    
    /* base を二乗して、指数を右にシフト */
    base ← (base × base) mod mod
    exp ← exp div 2
endwhile
ans を出力

【解答】


【解答】

24

【解説】

2^{10} = 1024 なので、1000で割った余りは 24 です。これをアルゴリズム通りにトレースします。

  1. 初期: base=2, exp=10 (1010), ans=1
  2. ループ1回目:
    • exp mod 2 は 0。ans そのまま。
    • base 更新: 2 \times 2 = 4
    • exp 更新: 10 \div 2 = 5 (0101)。
  3. ループ2回目:
    • exp mod 2 は 1。ans 更新: 1 \times 4 = 4
    • base 更新: 4 \times 4 = 16
    • exp 更新: 5 \div 2 = 2 (0010)。
  4. ループ3回目:
    • exp mod 2 は 0。ans そのまま(4)。
    • base 更新: 16 \times 16 = 256
    • exp 更新: 2 \div 2 = 1 (0001)。
  5. ループ4回目:
    • exp mod 2 は 1。ans 更新: 4 \times 256 = 1024
    • 1024 \pmod{1000} = 24ans24
    • base 更新: 256 \times 256 \dots (不要)
    • exp 更新: 1 \div 2 = 0
  6. ループ終了。24 が出力されます。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        long base = 2;
        long exp = 10;
        long mod = 1000;
        long ans = 1;

        while (exp > 0) {
            if (exp % 2 == 1) {
                ans = (ans * base) % mod;
            }
            base = (base * base) % mod;
            exp = exp / 2;
        }
        System.out.println(ans);
    }
}

第36問:Diffie-Hellman鍵交換法

【問題】

次のプログラムは、2者間(AliceとBob)で共通の秘密鍵を生成する「Diffie-Hellman鍵交換法」のアルゴリズムである。

Aliceの秘密値 secretA が 3、Bobの秘密値 secretB が 2 のとき、最終的に共有される鍵 keyA(または keyB)の値はいくつか。

※ modPow(base, exp, mod) は base^{exp} \pmod{mod} を計算する関数とする。

整数型: p ← 13  /* 素数 */
整数型: g ← 6   /* 生成元 */
整数型: secretA ← 3
整数型: secretB ← 2
整数型: publicA, publicB, keyA, keyB

/* 公開鍵の生成と交換 */
publicA ← modPow(g, secretA, p)
publicB ← modPow(g, secretB, p)

/* 相手の公開鍵と自分の秘密値から共通鍵を生成 */
keyA ← modPow(publicB, secretA, p)
keyB ← modPow(publicA, secretB, p)

keyA を出力

【解答】

12

【解説】

互いに相手の公開鍵を自分の秘密値で累乗することで、同じ値を共有できる仕組みです。

  1. Aliceの公開鍵: 6^3 \pmod{13} = 216 \pmod{13} = 8。 (publicA = 8)
  2. Bobの公開鍵: 6^2 \pmod{13} = 36 \pmod{13} = 10。 (publicB = 10)
  3. 共通鍵生成 (Alice側): publicB^{secretA} \pmod{p} = 10^3 \pmod{13} = 1000 \pmod{13}
    • 1000 \div 13 = 76 余り 12
  4. 確認 (Bob側): publicA^{secretB} \pmod{p} = 8^2 \pmod{13} = 64 \pmod{13}
    • 64 \div 13 = 4 余り 12
    • 両者とも 12 が生成されます。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        long p = 13;
        long g = 6;
        long secretA = 3;
        long secretB = 2;
        
        long publicA = modPow(g, secretA, p); // 8
        long publicB = modPow(g, secretB, p); // 10
        
        long keyA = modPow(publicB, secretA, p); // 10^3 % 13 = 12
        
        System.out.println(keyA);
    }

    public static long modPow(long base, long exp, long mod) {
        long res = 1;
        for (int i = 0; i < exp; i++) {
            res = (res * base) % mod;
        }
        return res;
    }
}


第37問:ヒープ構造(最大ヒープへの追加)

【問題】

次のプログラムは、配列 heap を「最大ヒープ(親は子より大きい)」として維持しながら要素を追加する関数 pushHeap である。

空のヒープに対し、データ 10, 30, 20, 5, 40 を順に追加した直後、配列 heap の先頭(根)の要素はいくつか。

整数型の配列: heap ← {}
整数型: size ← 0

○pushHeap(整数型: val)
    size ← size + 1
    整数型: k ← size
    heap[k] ← val
    
    /* 親と比較して、親より大きければ交換(アップヒープ) */
    while (k > 1 and heap[k] > heap[k ÷ 2])
        整数型: parent ← k ÷ 2
        整数型: temp ← heap[k]
        heap[k] ← heap[parent]
        heap[parent] ← temp
        k ← parent
    endwhile

【解答】

40

【解説】

最大ヒープでは、常に「最大の要素」が根(配列の先頭)に来ます。追加されるたびに親と比較して浮上していきます。

  1. push(10): {10}。根は10。
  2. push(30): 末尾に追加 {10, 30}。30 > 10 なので交換。 {30, 10}。根は30。
  3. push(20): 末尾に追加 {30, 10, 20}。20 < 30 (親) なのでそのまま。根は30。
  4. push(5): 末尾に追加 {30, 10, 20, 5}。5 < 10 (親) なのでそのまま。根は30。
  5. push(40): 末尾に追加 {30, 10, 20, 5, 40}
    • 親(10)と比較: 40 > 10 なので交換 → {30, 40, 20, 5, 10}
    • 親(30)と比較: 40 > 30 なので交換 → {40, 30, 20, 5, 10}
    • 根まで到達したため終了。根は 40 です。

【Javaコード】

import java.util.ArrayList;
import java.util.List;

public class Main {
    // 1始まりのインデックスを模倣するためにダミー要素を入れる
    static List<Integer> heap = new ArrayList<>();
    
    public static void main(String[] args) {
        heap.add(0); // index 0 (dummy)
        int[] data = {10, 30, 20, 5, 40};
        for (int val : data) {
            pushHeap(val);
        }
        System.out.println(heap.get(1)); // 根を出力
    }

    public static void pushHeap(int val) {
        heap.add(val);
        int k = heap.size() - 1;
        while (k > 1 && heap.get(k) > heap.get(k / 2)) {
            int parent = k / 2;
            int temp = heap.get(k);
            heap.set(k, heap.get(parent));
            heap.set(parent, temp);
            k = parent;
        }
    }
}


第38問:逆ポーランド記法(後置記法)

【問題】

次のプログラムは、逆ポーランド記法で書かれた配列 tokens を計算するものである。

tokens が {"3", "4", "+", "2", "*"} のとき、出力される結果はいくつか。

※ isDigit は数値かどうかを判定する関数、pop はスタックから取り出す、push は入れる処理とする。

文字列型の配列: tokens ← {"3", "4", "+", "2", "*"}
整数型のスタック: stack ← {}
整数型: a, b, i

for (i を 1 から tokensの要素数 まで 1 ずつ増やす)
    if (tokens[i] が数値である)
        push(stack, tokens[i]を整数に変換)
    else
        b ← pop(stack)
        a ← pop(stack)
        if (tokens[i] = "+") push(stack, a + b)
        elseif (tokens[i] = "*") push(stack, a * b)
        endif
    endif
endfor
pop(stack) を出力

【解答】

14

【解説】

逆ポーランド記法では、数値はスタックに積み、演算子が出たら数値を2つ取り出して計算し、結果を戻します。数式で表すと (3 + 4) * 2 です。

  1. "3": スタック [3]
  2. "4": スタック [3, 4]
  3. "+": 4, 3 を取り出し、3 + 4 = 7 をプッシュ。 スタック [7]
  4. "2": スタック [7, 2]
  5. "*": 2, 7 を取り出し、7 * 2 = 14 をプッシュ。 スタック [14]
  6. 最後にポップして 14 が出力されます。

【Javaコード】

import java.util.Stack;

public class Main {
    public static void main(String[] args) {
        String[] tokens = {"3", "4", "+", "2", "*"};
        Stack<Integer> stack = new Stack<>();
        
        for (String token : tokens) {
            if (token.matches("\\d+")) {
                stack.push(Integer.parseInt(token));
            } else {
                int b = stack.pop();
                int a = stack.pop();
                if (token.equals("+")) stack.push(a + b);
                else if (token.equals("*")) stack.push(a * b);
            }
        }
        System.out.println(stack.pop());
    }
}


第39問:幅優先探索(BFS)

【問題】

次のプログラムは、キューを用いてグラフを「幅優先探索 (BFS)」するものである。

ノード1から開始したとき、ノードが訪問(出力)される順序として正しいものはどれか。

※ adj は隣接リストを表す。

配列の配列: adj ← {
    {},        /* 0: 未使用 */
    {2, 3},    /* 1: 1から2, 3へ */
    {4, 5},    /* 2: 2から4, 5へ */
    {6},       /* 3: 3から6へ */
    {}, {}, {} /* 4, 5, 6: 行き先なし */
}
整数型のキュー: queue ← {}

enqueue(queue, 1) /* 開始ノード */

while (queue が空でない)
    整数型: n ← dequeue(queue)
    n を出力
    
    整数型: i
    for (i を 1 から adj[n]の要素数 まで 1 ずつ増やす)
        enqueue(queue, adj[n][i])
    endfor
endwhile

【解答】

1 → 2 → 3 → 4 → 5 → 6

【解説】

深さ優先探索(DFS)とは異なり、幅優先探索(BFS)は「近いところ」から順に、同じ深さの兄弟を全て訪問してから次へ進みます。

  1. キュー: {1}。 1を取り出し出力。子 2, 3 を追加。 キュー: {2, 3}
  2. 2を取り出し出力。子 4, 5 を追加。 キュー: {3, 4, 5}
  3. 3を取り出し出力。子 6 を追加。 キュー: {4, 5, 6}
  4. 4を取り出し出力。子なし。 キュー: {5, 6}
  5. 5を取り出し出力。子なし。 キュー: {6}
  6. 6を取り出し出力。子なし。 キュー: {}順序は 1, 2, 3, 4, 5, 6 となります。

【Javaコード】

import java.util.LinkedList;
import java.util.Queue;

public class Main {
    public static void main(String[] args) {
        int[][] adj = {
            {},
            {2, 3},
            {4, 5},
            {6},
            {}, {}, {}
        };
        Queue<Integer> queue = new LinkedList<>();
        queue.add(1);
        
        while (!queue.isEmpty()) {
            int n = queue.poll();
            System.out.println(n);
            for (int neighbor : adj[n]) {
                queue.add(neighbor);
            }
        }
    }
}


第40問:文字列探索(力まかせ法)

【問題】

次のプログラムは、文字列 text の中に、パターン pattern が含まれているかを探す処理である。

match が true(発見)になったとき、pattern の先頭文字が一致した text 内の位置 i はいくつか。

文字列型: text ← "BABABABC"
文字列型: pattern ← "ABC"
整数型: i, j
ブール型: match ← false

for (i を 1 から (textの文字数 - patternの文字数 + 1) まで 1 ずつ増やす)
    match ← true
    for (j を 1 から patternの文字数 まで 1 ずつ増やす)
        if (text[i + j - 1] ≠ pattern[j])
            match ← false
            break /* 内側のループを抜ける */
        endif
    endfor
    
    if (match = true)
        break /* 外側のループを抜ける */
    endif
endfor
i を出力

【解答】

6

【解説】

テキストの先頭から順に、パターンと一致するかを確認します。

  1. i=1 ("BAB..."): "B" vs "A" → 不一致。
  2. i=2 ("ABA..."): "A" vs "A"(一致), "B" vs "B"(一致), "A" vs "C"(不一致)。
  3. i=3 ("BAB..."): "B" vs "A" → 不一致。
  4. i=4 ("ABA..."): "A" vs "A"(一致), "B" vs "B"(一致), "A" vs "C"(不一致)。
  5. i=5 ("BAB..."): "B" vs "A" → 不一致。
  6. i=6 ("ABC"): "A" vs "A"(一致), "B" vs "B"(一致), "C" vs "C"(一致)。
    • ここで matchtrue のままループを完走します。
    • 答えは 6 です。

【Javaコード】

public class Main {
    public static void main(String[] args) {
        String text = "BABABABC";
        String pattern = "ABC";
        int n = text.length();
        int m = pattern.length();
        boolean match = false;
        int foundIndex = -1;

        for (int i = 0; i <= n - m; i++) {
            match = true;
            for (int j = 0; j < m; j++) {
                if (text.charAt(i + j) != pattern.charAt(j)) {
                    match = false;
                    break;
                }
            }
            if (match) {
                foundIndex = i + 1; // 1始まりに合わせる
                break;
            }
        }
        System.out.println(foundIndex);
    }
}