アルゴリズムと
データ構造
第2回
基本的なデータ構造
配列

配列の基本
 同じ型の要素の集合体
 要素の型は任意:算術型、ポインタ、構造体、共
用体、配列など
 基本型を単純に組み合わせたデータ構造
配列

配列の宣言と利用
int a[5];
/* int型で要素数5の配列を宣言 */
/* a[0]からa[4]まで利用可能 */
a[1] = 5;
/* a[1]に5を代入 */
x = a[3];
/* a[3]から値を取り出しxに代入 */
s = sizeof(a) / sizeof(a[0]); /* 配列の要素数をsに代入 */
int a[5] = {1, 2, 3, 4, 5};
/* 配列宣言時に初期化可能 */
動的メモリ管理

動的メモリ管理とは
 プログラム実行中に、記憶域を必要な分確保
 不要になった記憶域は開放

動的メモリ管理でできること
 プログラム開始後に配列領域の大きさを決定
 関数内で新規作成したデータの返却
動的メモリ管理

記憶域の確保
 malloc関数

void *malloc(size_t size)
sizeで指定したバイト数の記憶域を確保して返す
確保に失敗するとNULLが返される
 void

*型
どんな型も指定できる汎用ポインタ
動的メモリ管理

記憶域の確保
 calloc関数

void *calloc(size_t nmemb, size_t size)
sizeで指定したバイト数を単位として、要素nmemb個
の記憶域を確保して返す
確保された記憶域は0初期化される
確保に失敗するとNULLが返される
動的メモリ管理

記憶域の開放
 free関数を利用

void free(void *ptr)
ptrで指された、mallocで確保した領域を開放する
動的メモリ管理

動的配列の確保
char *str;
str = calloc(100, sizeof(char));
scanf(“%s”, str);
printf(“%s\”, str);
free(str);
・ char型100個分の領域を確保
・ 不要になったらfreeで開放するのを忘れずに
動的メモリ管理

動的配列の確保
int *make_array(int n) {
return (calloc(n, sizeof(int)));
}
・ n個の要素を持つint型配列を返す関数
・ 確保に失敗した場合はNULLが返される
・ この関数を呼んだ方で、使用後freeを実行する必要あり
配列要素の最大値

3値の最大値
int a, b, c;
int max;
max = a;
If (b > max) max = b;
If (c > max) max = c;
配列要素の最大値

要素3の配列の最大値
int a[3];
int max;
max = a[0];
If (a[1] > max) max = a[1];
If (a[2] > max) max = a[2];
配列要素の最大値

要素nの配列の最大値
int a[n];
int max;
/* 便宜上の宣言例 */
max = a[0];
If (a[1] > max) max = a[1];
If (a[2] > max) max = a[2];
・・・
If (a[n - 1] > max) max = a[n - 1];
配列要素の最大値

要素nの配列の最大値
int a[n];
int max, i;
/* 便宜上の宣言例 */
max = a[0];
for (i = 1; i < n; i++)
If (a[i] > max) max = a[i];
フローチャート
開始
a[0] → max
走査
i : 1, 1, n-1
Yes
a[i] > max
a[i] → max
No
走査
終了
配列要素の並びの反転
22
5
11
32
120
68
70
120
68
22
120
5
22
11
5
22
交換
70
5
11
32
交換
70
68
11
32
交換
70
68
120
32
n / 2回
配列要素の並びの反転

アルゴリズムの流れ
先頭と最後尾を交換
2. それぞれ1つずつ内側を交換
3. 以下中央に来るまで繰り返し
1.
for (i = 0; i < n / 2; i++)
a[i] と a[n - i - 1] を交換
配列要素の並びの反転

2値の交換
 作業用に同じ型の変数を介して交換
xとyを交換するとき、作業用変数tを用意した場合:
1.
xの値をtに代入
2.
yの値をxに代入
3.
tの値をyに代入
配列要素の並びの反転

反転プログラム
int i;
for (i = 0; i < n / 2; i++) {
int t = a[i];
a[i] = a[n - i - 1];
a[n - i - 1] = t;
}
基数変換

10進整数からn進整数へ
 変換する数をnで割った剰余を求める
剰余を求めたら商に対して順次繰り返し
 商が0になったら終了

10
1962
8
1962
16
1962
10
196 2
8
245 2
16
122 10
10
19 6
8
30 5
16
7 10
10
1 9
8
3 6
0 1
1962(10)
0 7
0 3
3652(8)
7AA(16)
基数変換

変換プログラム
int card_convr(unsigned x, int n, char d[]) {
char dchar[] = “0123456789ABC...XYZ”; /* 一部省略 */
int digits = 0; /* 変換後の桁数 */
do {
d[digits++] = dchar[x % d];
x /= n;
} while (x);
return digits;
}
素数の列挙

基本アルゴリズム
 整数nが、2からn-1までの整数で割り切れるかを
総チェック
割り切れる数があったら素数ではないと判断
 割り切れた段階でそれ以降の判定は不要

素数の列挙

アルゴリズムの改良(1)
 整数nが、nより小さい素数で割り切れるかのみを
チェック
2で割り切れなければ、4(2x2)や6(2x3)で割り切れな
いのは明らか
 素数と判定されたものを順次配列に格納し、それらの
値でのみ割り切れるかの判定

素数の列挙

アルゴリズムの改良(2)
 整数nが、nの平方根より小さい素数で割り切れ
るかのみをチェック
割り切れる数は、nの平方根までの数ですべて列挙
 エラトステネスのふるい

多次元配列

配列を要素に持つ配列
 配列が要素:2次元配列
 2次元配列が要素:3次元配列
 n-1次元配列が要素:n次元配列
多次元配列

2次元配列の宣言と利用
/* 「int型で要素数6の配列」を要素に持つ */
/* 要素数3の配列を宣言 */
/* a[0][0]からa[2][5]まで利用可能 */
a[1][3] = 5; /* a[1][3]に5を代入 */
x = a[2][1]; /* a[2][1]から値を取り出しxに代入 */
r = sizeof(a) / sizeof(a[0]); /* aの行数をrに代入 */
c = sizeof(a[0]) / sizeof(a[0][0]); /* aの列数をcに代入 */
int a[3][6];
多次元配列

多次元配列の記憶域上での配置
 直線状に格納
まず一番後ろの添え字が増加
 最後の添え字にいたると、1つ前の添え字が増加

多次元配列

年内の経過日数の計算
 当該月の前月までの日数合計と当該日の和
4月20日:1月、2月、3月それぞれの日数 + 20
 うるう年の扱い
 平年時とうるう年時で2月の日数を切り替え→切り替
えに2次元配列の利用

多次元配列

切り替え用2次元配列
 平年時:0行目の要素を利用
 うるう年時:1行目の要素を利用
平年
31
28
31
30
31
30
31
31
30
31
30
31
うるう年
31
29
31
30
31
30
31
31
30
31
30
31
構造体

構造体の基本
 関連あるデータを1つの単位として取り扱い

複数個のデータをばらばらの配列の寄せ集めで扱わ
ずにすむ
 任意の型を組み合わせて作成することが可能
構造体

構造体の宣言
struct xyz { /* 構造体xyzの型宣言 */
int x;
/* xyz:構造体タグ */
long y;
/* x, y, z:構造体メンバ */
double z;
};
struct xyz s; /* sを構造体xyzとして生成 */
構造体

構造体の利用
s.x = 1;
t = s.y;
/* sのメンバxに1を代入 */
/* tにsのメンバyを代入 */
struct xyz *p; /* pを構造体xyzのポインタとして生成 */
p = &s;
/* pがsを指すようにする */
p->x = 2;
/* pのメンバxに2を代入 */
構造体

構造体の配列
struct xyz a[3];
/* 構造体xyz型で要素数3の配列を宣言 */
a[0].x = 3;
/* a[0]のメンバxに3を代入 */
t = a[2].y;
/* tにa[2]のメンバyを代入 */
s = sizeof(a) / sizeof(a[0]); /* sにaの要素数を代入 */
ダウンロード

講義資料ppt