Transcript 講義資料ppt
アルゴリズムと
データ構造
第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の要素数を代入 */