C++ 複数入力。defineの使い方からバブルソートとセレクションソートなどの並び替え
バブルソート
複数入力をするとどうなるのか
要はこれです。どうなるのか
int main() { string a; int b; cin >> a >> b; cout << a << endl; cout << b << endl; return 0; }
実行結果
普通にEnterを押して複数個入力できるようです。
意外と楽ですね。
ソート人数の設定
一つの方法として#defineを使って最初に人数を定義しておく。
こんな感じ
#include<iostream> #include<string> #define NUM 5 using namespace std; int main() { return 0; }
こうすることでNUMが5という値になります。データの数を先に決めておく的な。
実行結果出すまでもないので・・・
最初にNUMをすることで、この後のプログラムを書く際にNUMの定義を変えれば人数を変えれることになります。柔軟になるということですね。
とりあえずこのままバブルソートへ
まずはNUM回の入力から。名前も入れましょうか。
#include<iostream> #include<string> #define NUM 5 using namespace std; int main() { string name[NUM]; int score[NUM]; for (int i = 0; i < NUM; i++) { cout << i + 1 << "人目の名前を入力してください" << endl; cin >> name[i]; cout << i + 1 << "人目の点数を入力してください" << endl; cin >>score[i]; } for (int i = 0; i < NUM; i++) { cout << i + 1 << ":" << name[i] << " " << score[i] << endl; } return 0; }
実行結果
上手くいっていますね。
今回のソートは昇順で流していくことにしましょう。昇順ができれば降順に変える方法は簡単なので。
同時に最大・最小も出力しましょう。
以上纏めると今回やることは
- バブルソートを行い、昇順にする
- 最高点の名前・点数を出力
- 最低点の名前・点数を出力
ではまず、バブルソートから
#include<iostream> #include<string> #define NUM 5 using namespace std; int main() { string name[NUM]; int score[NUM]; for (int i = 0; i < NUM; i++) { cout << i + 1 << "人目の名前と点数を入力してください" << endl; cin >> name[i]; cin >>score[i]; } for (int i = 0; i < NUM; i++) { for (int j = NUM - 1; j > i; j--) { if (score[j-1]>score[j]) { int a = score[j - 1]; score[j - 1] = score[j]; score[j] = a; string b = name[j - 1]; name[j - 1] = name[j]; name[j] = b; } } } for (int i = 0; i < NUM; i++) { cout << i + 1 << ":" << name[i] << " " << score[i] << endl; } return 0; }
実行結果
上手くいっていますね。
次は最大最小です。
といっても簡単で、これは最大・最小が更新されるたびにその[i]の値を残しておけばいいでしょう。
int型でmaxindexとminindexを宣言します。初期値を0にすることで、データが1個の時の最大値・最小値は1人目の点数になります。
#include<iostream> #include<string> #define NUM 5 using namespace std; int main() { string name[NUM]; int score[NUM]; int maxindex = 0; int minindex = 0; for (int i = 0; i < NUM; i++) { cout << i + 1 << "人目の名前と点数を入力してください" << endl; cin >> name[i]; cin >>score[i]; if (score[i] > score[maxindex]) { maxindex = i; } if (score[i] < score[minindex]) { minindex = i; } } cout << "最高点は" << name[maxindex] << " " << score[maxindex]<<endl; cout << "最低点は" << name[minindex] << " " << score[minindex]<<endl; for (int i = 0; i < NUM; i++) { for (int j = NUM - 1; j > i; j--) { if (score[j-1]>score[j]) { int a = score[j - 1]; score[j - 1] = score[j]; score[j] = a; string b = name[j - 1]; name[j - 1] = name[j]; name[j] = b; } } } for (int i = 0; i < NUM; i++) { cout << i + 1 << ":" << name[i] << " " << score[i] << endl; } return 0; }
実行結果
上手くいっていますね
乱数を使ってデータを入力する。
なんだか、AさんとかBさんとか毎回入力するのもバカバカしくなってきたので、ここも自動でやってもらいます。
C++にforeachのような構文があるのかよくわからないので、ここは配列を普通に使用してrandを使えば勝手に名前と点数を入れてくれるかな。
#include<iostream> #include<string> #define NUM 5 using namespace std; int main() { string name[NUM] = { "A","B","C","D","E" }; int score[NUM]; int maxindex = 0; int minindex = 0; for (int i = 0; i < NUM; i++) { cout << name[i] << "さんの点数:"; score[i] = (rand() % 100) + 1; cout << score[i] << endl; if (score[i] > score[maxindex]) { maxindex = i; } if (score[i] < score[minindex]) { minindex = i; } } cout << "最高点は" << name[maxindex] << " " << score[maxindex]<<endl; cout << "最低点は" << name[minindex] << " " << score[minindex]<<endl; for (int i = 0; i < NUM; i++) { for (int j = NUM - 1; j > i; j--) { if (score[j-1]>score[j]) { int a = score[j - 1]; score[j - 1] = score[j]; score[j] = a; string b = name[j - 1]; name[j - 1] = name[j]; name[j] = b; } } } for (int i = 0; i < NUM; i++) { cout << i + 1 << ":" << name[i] << " " << score[i] << endl; } return 0; }
実行結果
随分と楽になりました。
ここから先は名前とかどうでもいい(大学などで課題で出る場合は必要なのでしょうが)ので、点数のみでソートをかけることにしましょう。コードも少し長くなって見づらくなってきたので。
NUM 10に変更し
nameを消しました。
#include<iostream> #include<string> #define NUM 10 using namespace std; int main() { int score[NUM]; for (int i = 0; i < NUM; i++) { score[i] = (rand() % 100) + 1; cout<< i+1 <<":"<< score[i] << endl; } cout << endl; for (int i = 0; i < NUM; i++) { for (int j = NUM - 1; j > i; j--) { if (score[j-1]>score[j]) { int a = score[j - 1]; score[j - 1] = score[j]; score[j] = a; } } } for (int i = 0; i < NUM; i++) { cout << i + 1 << ":" <<score[i] << endl; } return 0; }
実行結果
いい感じですね。
それではセレクションソートへ。
セレクションソート
これは、最小(又は最大)の選び出し、昇順ならば一番上に吹っ飛ばし、次は一番上以外の数字の中で最小の数を探し、二番目にぶっ飛ばすソートです。
先ほどと同様、minindexを導入しましょう。
内部ループ(jのループ)では直接のデータ交換は無しです。あくまで最小の値を持つindexを保存するだけです。
外部ループ(iのループ)でデータを交換してください。
#include<iostream> #include<string> #define NUM 10 using namespace std; int main() { int score[NUM]; int minindex; for (int i = 0; i < NUM; i++) { score[i] = (rand() % 100) + 1; cout<< i+1 <<":"<< score[i] << endl; } cout << endl; for (int i = 0; i < NUM; i++) { minindex = i; //初期化 for (int j = i; j < NUM; j++) { if (score[minindex] > score[j]) { minindex = j; } } int a = score[i]; score[i] = score[minindex]; score[minindex] = a; } for (int i = 0; i < NUM; i++) { cout << i + 1 << ":" <<score[i] << endl; } return 0; }
実行結果
と、セレクションソートでした。
次はNUMの値を任意に変えても繰り返せるようになりたいですね。
C++ クラスと関数の扱い。public と private
今、練習中に気づきましたが
C++でarrayで安易にsizeofを使おうとすると大変なことになりますね。
int a[5]; for(int i = 0; i < sizeof(a);i++){ cout<<a[i]<<endl; }
とかやろうとすると、上手くいかないんですね。
こういうときは
sizeof(array) / sizeof(array[0])
を使うようですね。
実際
#include<iostream> #include<string> using namespace std; int main() { int a[5] = { 0,0,0,0,0 }; cout << sizeof(a) << endl; return 0; }
とすると
20らしいです。
sizeof(array) / sizeof(array[0])
を使うと
となっていい感じです。
これはもう定義しておきましょう。
#include<iostream> #include<string> #define ARRAY_LENGTH(array) (sizeof(array) / sizeof(array[0])) using namespace std; int main() { int a[5] = { 0,0,0,0,0 }; cout << ARRAY_LENGTH(a) << endl; return 0; }
クラスと関数
クラスの作り方ですが、すごく適当に言えばこんな感じ。
#include<iostream> #include<string> #define ARRAY_LENGTH(array) (sizeof(array) / sizeof(array[0])) #define NUM 5 using namespace std; class Stack{ public: int value = rand() % 100 + 1; }; int main() { Stack a; Stack b; return 0; }
{}を閉じるタイミングがすごく慣れないです。
これでStackクラスが出来ました。したのint main()の所でStackクラスの変数を2つ宣言しています。
このaとbはvalueの値を持っているので、ピリオドで繋ぐことで使うことが出来ます。出力してみると
#include<iostream> #include<string> #define ARRAY_LENGTH(array) (sizeof(array) / sizeof(array[0])) #define NUM 5 using namespace std; class Stack{ public: int value = rand() % 100 + 1; }; int main() { Stack a; Stack b; cout << "a=" << a.value << endl; cout << "b=" << b.value << endl; return 0; }
いい感じですね。
ここでpublicとprivateの差に入ります
publicとprivate
とりあえず、public→privateへ変換
#include<iostream> #include<string> #define ARRAY_LENGTH(array) (sizeof(array) / sizeof(array[0])) #define NUM 5 using namespace std; class Stack{ private: int value = rand() % 100 + 1; }; int main() { Stack a; Stack b; cout << "a=" << a.value << endl; cout << "b=" << b.value << endl; return 0; }
これで実行しようとするとビルドエラー
こんなエラーが出ています
エラー C2248 'Stack::value': private メンバー (クラス 'Stack' で宣言されている) にアクセスできません。
要は、privateにするとStackクラス内からしか操作できなくなります。もちろん出力だけも禁止です。今回エラーの原因は15,16行目ですね。
なので、出力する関数をStackクラス内に書きます。
class Stack{ private: int value = rand() % 100 + 1; public: int getvalue(); }; int Stack::getvalue() { return value; }
といって取得する関数でもいいですし
class Stack{ private: int value = rand() % 100 + 1; public: void getvalue(); }; void Stack::getvalue() { cout<<value<<endl; }
でもいいです。
ここで、クラス内でちゃんと返り値などの定義もしてあげないと動かないので(これが分かるのに時間かかった・・・)注意してください。
set等も同様に行うことで、関数を経由しないと数値が操作できなくなります。勝手に値を変えられないように制限するために使ったりするのかな?
#include<iostream> #include<string> #define ARRAY_LENGTH(array) (sizeof(array) / sizeof(array[0])) #define NUM 5 using namespace std; class Stack{ private: int value; public: int getvalue(); void setvalue(); }; int Stack::getvalue() { return value; } void Stack::setvalue() { cin >> value; } int main() { Stack a; Stack b; a.setvalue(); b.setvalue(); cout << "a=" << a.getvalue() << endl; cout << "b=" << b.getvalue() << endl; return 0; }