このページではJavaの演習問題をいくつか用意した。
特にアルゴリズムを考える問題に絞ってこのページではまとめている。Javaを勉強中の方はチャレンジしてみてほしい。
目次
演習問題の前提
本編では、プログラミング演習に関する焦点を、アルゴリズム部分に絞るため、プログラムのひな型として、下記を使用するものとする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import java.io.BufferedReader; import java.io.InputStreamReader; public class Main { public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // 入力パラメータを読み込む String input = br.readLine(); String[] param = input.split(" ");//入力値を空白で分解する /** * ここに回答を記述する **/ } } |
- 入力値は空白で区切られ、変数paramに代入される。
- 入力値は問題で定められた規則に従い、正しく入力されるものとし、入力ミスなどは考慮しなくてよい。
Javaアルゴリズム演習問題
それではJavaの演習問題を解いて行こう。回答は最後に載っているが、すぐに見ないでトライしてみていただきたい。
問題1(配列)
入力された2つの数の和を出力するプログラムを作成しなさい。
- 入力する数は、共に整数とする。
- 負の数も含む。負の数の場合は、数値の直前に空白を挟まずに「-」(マイナス)を付加して入力するものとし、数値の範囲は-10000~10000とする。
入力例:
5 4
出力例:
9
問題2(分岐/多重分岐)
入力された四則演算の結果を出力するプログラムを作成しなさい。下記が条件だ。
- 入力する数は、すべて整数とする。
- 演算記号は「+」(和) 「-」(差) 「*」(積) 「/」(商)を使用する。
- 四則演算の式は、1回の演算とし、2回以上の演算は行わない。(正しい:23 * 58 / 誤り: 23 + 58 + 7)
- 商を求める際は、余りを「 … N」と表示する
- 「0」(ゼロ)で割る式は入力しない
- 負の数も含む。負の数の場合は、数値の直前に空白を挟まずに「-」(マイナス)を付加して入力するものとし、数値の範囲は-10000~10000とする。
入力例
23 * 58
出力例
1334
問題3(繰り返し)
入力された加算/減算演算の結果を出力するプログラムを作成しなさい。
- 入力する数は、すべて整数とする。
- 演算記号は「+」(和) 「-」(差)を使用する。
- 演算の式は、最低1回の演算を行うものとし、複数回の演算を行う場合もある。(正しい: 23 + 58 + 7)
- 負の数も含む。負の数の場合は、数値の直前に空白を挟まずに「-」(マイナス)を付加して入力するものとし、数値の範囲は-10000~10000とする。
- 演算の途中経過における取りうる値の反は-10000~10000とする。
入力例
23 + 58 + 7
出力例
88
問題4(再帰)
入力された加算/減算演算の結果を出力するプログラムを作成しなさい。
- 入力する数は、すべて整数とする。
- 演算記号は「+」(和) 「-」(差)を使用する。
- 演算の式は、最低1回の演算を行うものとし、複数回の演算を行う場合もある。
- 四則演算の記法に乗っ取り、先に行うべき演算は「(」および「)」でくくるものとし、入れ子構造も可とする。【正しい: 23 + 58 + 7 – ( 10 + 3 ) – ( 3 – ( 5 + 4 – ( 8 – 2 ) ) )】
- 負の数も含む。負の数の場合は、数値の直前に空白を挟まずに「-」(マイナス)を付加して入力するものとし、数値の範囲は-10000~10000とする。
- 演算の途中経過における取りうる値の反は-10000~10000とする。
入力例
23 + 58 + 7 - ( 10 + 3 ) - ( 3 - 2 - ( 5 + 4 - ( 8 - 2 ) + 8 ) )
出力例
85
演習問題解答
ここから下は演習問題の解答だ。トライしてみてから、確認しよう。
問題1 解答/解説
解答例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import java.io.BufferedReader; import java.io.InputStreamReader; public class Main { public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // 入力パラメータを読み込む String input = br.readLine(); String[] param = input.split(" ");//入力値を空白で分解する int num1 = Integer.parseInt(param[0]);//① int num2 = Integer.parseInt(param[1]); System.out.println(num1 + num2); } } |
①:入力パラメータを数値に変換します。文字列を数値に変換する際、文字列が「-N」の形式の場合は、負の値として変換される。
まずは、入力された数値を加算するだけのシンプルな問題だ。
注意すべきポイントは
・配列の添え字は「0」(ゼロ)から始まる。
というところだ。
問題2 解答/解説
解答例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import java.io.BufferedReader; import java.io.InputStreamReader; public class Main { public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // 入力パラメータを読み込む String input = br.readLine(); String[] param = input.split(" ");//入力値を空白で分解する int num1 = Integer.parseInt(param[0]); String type = param[1] ; int num2 = Integer.parseInt(param[2]); if(type.equals("+")){ //① System.out.println(num1 + num2); }else if(type.equals("-")){ System.out.println(num1 - num2); }else if(type.equals("*")){ System.out.println(num1 * num2); }else if(type.equals("/")){ System.out.println(num1 / num2 + " ... " + num1 % num2);//② } } } |
- ①:文字列の比較は、「==」では無く、Stringクラスのequals()メソッドを使用する。
- ②:割り算の余りを求める際には、「%」演算子を使用する。
条件判定を行い、その結果に応じて結果を出力すればよい。ポイントとしては、
if/elseを使用して
1 2 3 4 5 |
if(…){ }else if(…){ } |
のように、多重分岐のパターンを網羅する辺りだろう。
また、開発環境がJDK1.7以降であれば、switch文にString型を使用する事が出来るので、下記のように記述しても良い。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
switch(type){ case "+" : System.out.println(num1 + num2); break; case "-" : System.out.println(num1 - num2); break; case "*" : System.out.println(num1 * num2); break; case "/" : System.out.println(num1 / num2 + " ... " + num1 % num2); break; } |
switch文は、if/else ifに比べて、多重分岐の記述が容易であるというメリットがあるが、使用頻度が低く、記述ミスを生みやすいので、十分注意してもらいたい。
問題3 解答/解説
解答例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import java.io.BufferedReader; import java.io.InputStreamReader; public class Main { public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // 入力パラメータを読み込む String input = br.readLine(); String[] param = input.split(" ");//入力値を空白で分解する String type = null ; Integer sum = 0 ; for(int idx = 0 ; idx < param.length; idx ++){//① if(param[idx].equals("+") || param[idx].equals("-")){//② type = param[idx] ; }else{ Integer num = Integer.parseInt(param[idx]);//③ if(type != null){//④ if(type.equals("+")){ sum = sum + num; }else if(type.equals("-")){ sum = sum - num; } }else{ sum = num ;//⑤ } } } System.out.println(sum); } } |
- ①:入力パラメータを順に処理する。
- ②:入力パラメータが演算記号の場合、演算記号を変数に格納する。
- ③:入力パラメータが演算記号でない場合、数値に変換する。
- ④:上記③で演算記号が格納されている場合、これまでの合計値と、その演算記号に応じた演算を行い、合計値として設定する。
- ⑤:上記③で演算記号が格納されていない場合(=先頭の数字の場合)、その値を合計値として設定する。
入力する数値の個数が決まっていないため、繰り返し処理を必要とする。(①の部分)
基本的な考え方として、処理の対象となる文字列が演算記号なのか(②)、そうでない数字(③)なのかで分岐し、数字の場合は指定された演算記号に応じた演算を行う(④)。
ただし、先頭の数字の場合は、まだ演算記号が登場していない。(⑤の部分)
従って演算は行わずに、演算結果として合計値を保存する変数に代入する。
繰り返し処理の場合には、繰り返しの全ケースで同じことが出来るとは限らず、例外的なケースを漏らさず見つけることが重要だ。
問題4 解答/解説
解答例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
import java.io.BufferedReader; import java.io.InputStreamReader; public class Main { public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // 入力パラメータを読み込む String input = br.readLine(); String[] param = input.split(" ");//入力値を空白で分解する int sum = calc(param,0,param.length - 1) ; System.out.println(sum); } public static int calc(String[] param, int startIdx, int endIdx){ int sum = 0 ; String type = null ; for(int idx = startIdx ; idx < endIdx + 1 ; idx ++){ if(param[idx].equals("+") || param[idx].equals("-")){ type = param[idx] ; }else if(param[idx].equals("(")){ int endOfFormula = checkEndOfFormula(param,idx) ;//③ if(type != null){ if(type.equals("+")){ sum = sum + calc(param, idx + 1 , endOfFormula - 1);//① }else if(type.equals("-")){ sum = sum - calc(param, idx + 1 , endOfFormula - 1);//② } }else{ sum = calc(param, idx + 1 , endOfFormula - 1) ; } idx = endOfFormula ; }else{ int num = Integer.parseInt(param[idx]); if(type != null){ if(type.equals("+")){ sum = sum + num; }else if(type.equals("-")){ sum = sum - num; } }else{ sum = num ; } } } return sum ; } //かっこ「(」の終わりを見つける public static int checkEndOfFormula(String[] param, int startIdx){ int end = startIdx + 1; int startCount = 0 ; for(int idx = startIdx + 1 ; idx < param.length; idx ++){ end = idx ; if(param[idx].equals("(")){ startCount ++ ;//④ }else if(param[idx].equals(")")){ if(startCount == 0){ break ; }else{ startCount -- ;//⑤ } } } return end ; } } |
- ①、② calc()メソッドを、再帰的に呼び出す。
この問題の場合はまず考え方を整理する必要がある。
例えば、「7 – ( 5 – 2 )」という入力を考えたとき、左から順に計算をしていくと、7から引くべき数は( 5 – 2 )となるため、最初に 5 – 2の計算をしなければならない。
もちろん、( ) を入れ子にすることが出来るので、さらに小分けにする可能性も出てくる。
そこで、計算式を解析する処理をcalc()メソッドに分け、その中で「(」を見つけた場合には、その『「(」の終わりまでの部分を別で計算する為にcalc()メソッドを呼ぶ』という再帰処理を作成する事にする。
calc()メソッドは、パラメータ配列と、計算をする先頭と終端の添え字を渡すことで結果を返してくれるものだ。
例えば、入力が先の【 7 – ( 5 – 2 ) 】だとすると、パラメータ配列は以下のようになる。
添え字 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
パラメータ配列 |
7 |
- |
( |
5 |
- |
2 |
) |
この中の「 5 - 2」の部分を計算する場合、計算をする先頭の添え字「3」と終端の添え字の「5」を渡すことで、5 - 2を計算してくれる。(①、②の部分)
calc()メソッドは、その中で再帰的にcalc()メソッドを呼び出しているため、「(」を見つけた場合は、その「(」の終端を見つけ、calc()メソッドにその間の部分を計算するように呼び出せばよい。
「(」の終端を見つけるためにはcheckEndOfFormula()メソッドを利用する。(③の部分)
「(」の終端とは、単に「)」を見つけるのではなく、入れ子構造となている可能性を考慮する。
例えば、
3 – ( 5 – ( 2 + 1 ) )
のような式を考える。
添え字 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
パラメータ配列 |
3 |
- |
( |
5 |
- |
( |
2 |
+ |
1 |
) |
) |
この場合、添え字「2」の「(」の終端は、添え字「9」では無く「10」の「)」のほうだ。
しかし、見た目では分かるのだが、プログラムとするためには途中の( )をどう扱うかがポイントだ。
模範解答では、④の部分で途中に現れた「(」の数をカウントし、⑤では「」」が現れると減算している。
これは、途中に「(」や「)」が何個あろうと、必ず同じ数だけあるため、( )が終わっていれば、カウントは「0」(ゼロ)になるという発想からきている。
このように、( )の位置を判定するcheckEndOfFormula()メソッドと、計算を行うcalc()メソッドを利用する事で、計算結果を算出する事が出来る。
まとめ
このページではJavaでアルゴリズムを考えるような問題を用意してみた。初心者の方向けなので、そこまで難しくなかったかもしれない。後半は徐々に難易度が上がっていくだろう。
ぜひ理解度やプログラミングの応用力をチェックするのに役立ててほしい。
全然、分かりません。頭痛いです。