Transcript URL

アルゴリズムと
データ構造
第4回
基本的なデータ型(3):
多次元配列, 構造体
配列によるリスト
1
前回の復習


動的メモリ管理
配列の応用
 整数の動的生成
 配列要素の最大値を求める関数
 配列要素の並びの反転
 基数変換
2
多次元配列

配列を要素に持つ配列



配列が要素:2次元配列
2次元配列が要素:3次元配列
n-1次元配列が要素:n次元配列
3
二次元配列

行と列それぞれをインデックスで指し示す
1
2
1
(3,2)
添え字を
用いて
データに
アクセス
n
m
・・・・・・・
3
・
・
・
・
・・・
・・・・・・・
2
4
3
・
・
・
・
・
・
・
・
・
・
・
・
・
・
・
・
・・・・・・・・・・・・・・・・・
4
三次元配列
1
2
3
・・・・
・・・・・
・
・
・
・
・
・
・
・
・
・・・・・・・・・
1
2
3
・
・
・
k
・
・
・
・
・
・
・
・
・
m
・
・
・
・
・
・
・
・
・
(3,1,1)
添え字を
用いて
データに
アクセス
5
多次元配列

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];
6
多次元配列

多次元配列の記憶域上での配置

直線状に格納


まず一番後ろの添え字が増加
最後の添え字にいたると、1つ前の添え字が増加
例) 2次元配列 a[3][3]の場合
2次元配列のイメージ
a[0][0]
a[0][1]
a[0][2]
a[1][0]
a[1][1]
a[1][2]
a[2][0]
a[2][1]
a[2][2]
実際の記憶域上
a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] a[2][0] a[2][1] a[2][2]
7
多次元配列

年内の経過日数の計算

当該月の前月までの日数合計と当該日の和



4月20日:1月、2月、3月それぞれの日数 + 20
うるう年の扱い
平年時とうるう年時で2月の日数を切り替え→切り替えに2次元配列の利
用
8
多次元配列

切り替え用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
9
年内の経過日数の計算
10
年内の経過日数の計算
実行例
入力
年: 2015
月: 4
日: 15
出力
年内で105日目です。
入力
もう一度しますか(1…はい/0…いいえ): 0
11
構造体

構造体の基本

関連あるデータを1つの単位として取り扱う


複数個のデータをばらばらの配列の寄せ集めで扱わずにすむ
任意の型を組み合わせて作成することが可能
学籍番号
学籍番号
名前
学籍番号
名前
生年月日
学籍番号
名前
生年月日
学籍番号
体重
名前 名前
生年月日
体重 名前
身長生年月日
体重数学の点数
身長
生物学の点数
生年月日
体重
身長
地学の点数
体重
身長
英語の点数
身長
国語の点数
点数の平均
struct SEISEKI {
char name[100];
float suugaku;
float seibutsugaku;
float chigaku;
float eigo;
float kokugo;
float heikin;
};
struct SEISEKI p[10];
12
構造体

構造体の宣言
struct xyz { /* 構造体xyzの型宣言 */
int x;
/* xyz:構造体タグ */
long y;
/* x, y, z:構造体メンバ */
double z;
};
struct xyz s; /* sを構造体xyzとして生成 */
13
typedefの使用方法
struct SEISEKI {
char name[100];
float suugaku;
float seibutsugaku;
float chigaku;
float eigo;
float kokugo;
float heikin;
};
int main()
{
struct SEISEKI p[10];
-処理-
}
宣言するとき,
“struct 構造体名”と書く必要がある
typedef struct {
char name[100];
float suugaku;
float seibutsugaku;
float chigaku;
float eigo;
float kokugo;
float heikin;
} seiseki;
int main()
{
seiseki p[10];
-処理-
}
宣言するとき,
struct が不要になる.
14
“seiseki”が型として使えるため.
構造体のメンバーの参照
変数実体を操作する時 .
ポインタで操作する時
を使う(ドット演算子)
->
を使う(アロー演算子)
y=&x;
typedef struct {
int a;
float b;
} abcd;
x のアドレス( &x) を
y に代入する
データの参照:
x.a
y->a 同じ
(*y).a
abcd x, *y;
変数
ポインタ
宣言
int a;など
int *a;など
実体
a
*a
アドレス
&a
a
x.b
y->b
(*y).b
同じ
y->a は (*y).a の略
15
構造体

構造体の利用の例
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を代入 */
16
構造体の配列
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の要素数を代入 */
17
構造体の配列の動的生成
typedef struct {
int x;
int y;
} abcd;
void main(){
abcd *p;
先頭ポインタ
n個×(abcd型のバイト数)
=確保すべき領域のバイト数
p=(abcd*)malloc(n*sizeof(abcd)); これでp[n]が確保された!
失敗すると
NULLを返す
if (p==NULL) {
printf(”メモリの確保はできなかった\n” );
exit(1);
}
-処理-
free(p);
}
18
基本的な抽象データ型
3つとも情報関連の資格試験
に頻出する.重要なのでよく理
解しておくこと.

リスト(List)

リストの特殊なケース
 スタック(Stack)
 キュー(Queue,
D
C
B
A
待ち行列)
D C B A
19
リスト

リスト

データが順序づけられて並んだデータ構造



最も単純なもの:線形リスト(連結リスト)
単連結リスト(単方向リスト、一方向リストとも)
ノード(要素)



線形リスト上の個々のデータ
最初のノードは先頭ノード、最後のノードは末尾ノード
1つ前のノードは先行ノード、1つ後のノードは後続ノード
a0, a1, a2, …, ai-1, ai, ai+1, … , an-1
先頭ノード
aiの先行ノード
末尾ノード
aiの後続ノード
ai : i 番目の位置にある要素
n : 要素数.リストの長さ.
n = 0のとき空リストという
20
抽象データ型としてのリスト

抽象データ型: データ構造+操作
リスト
操作
新要素の挿入
要素を
並べたもの
要素の削除
リストを空にする
21
表記法についての説明

L
p

x

: リスト
:位置を表わす変数
:挿入したいデータ
要素の型はすべて同じであれば
よい.
x
L
a0
a1
p
…
ai-1
n
ai
…
an-1
22
代表的なリスト操作
 INSERT(x, p, L) 新要素の挿入
 DELETE(p, L) 要素の削除
 LOCATE(x, L) 要素xの位置を探す
 RETRIEVE(p, L) 位置pにある要素を返す
 FIND(i, L)
i番目の要素を返す
 TOP(L)、LAST(L) 先頭、末尾の位置を返す
 NEXT(p, L)、PREVIOUS(p, L) pの直前、直後の位置を返す
 CREATE(L) 新規に空リストLを作成する
23
INSERT(x, p, L)
 リストLの位置pの次に要素xを挿入
p
L
A
B
C
D
x
E
F
G
x
E
F
p
L
A
B
C
D
G
24
DELETE(p, L)
 リストLの位置pの次の要素(もし存在すれば)を削除
p
L
A
B
C
D
E
F
G
H
p
L
A
B
C
D
E
F
G
H
25
LOCATE(x, L)

要素xがL中に存在すれば、その位置を返す
X
L
A
B
C
X
F
G
H
この位置が返る
配列のインデックスなら[3]
26
RETRIEVE(p, L)

位置pのセルの内容を返す
p
L
A
B
C
D
F
G
H
‘ F ’が返る
27
FIND(i, L)

L の i 番目のセルの内容を返す(ただし、i は0から始まるも
のとする)
i = 2のときは?
L
A
B
C
D
F
G
H
添字
0
1
2
3
4
5
6
‘ C ’が返る
28
TOP(L) と LAST(L)
TOP(L)
L の最初の位置を返す
LAST(L)
L の最後の位置を返す
TOP( L )
L
A
B
LAST( L )
C
D
F
G
H
29
NEXT(p,L)とPREVIOUS(p,L)
NEXT(p, L)
位置pの次のセルの位置を返す
PREVIOUS(p, L)
位置pの前のセルの位置を返す
p
L
A
B
C
PREVIOUS(p, L )
D
F
G
H
NEXT(p, L )
30
CREATE(L)
CREATE(L)
空リスト L を準備し、その先頭の位置を返す
空リストの先頭の位置が返る
L
31
リストの実現(実装)
すべての操作が常に必要になるとは限らない
 すべての操作を効率よく実行できるデータ構造の実現は困難
⇒プログラムの実装の際には,必要な操作を,効率よく実行でき
るデータ構造を採用する


リストの実現に使用できるデータ構造
 配列
 連結リスト(linked list)

ポインタを用いてデータ間の関係を表現したデータ構造
Insert,Deleteの実現方法の例を説明する
32
配列によるリストの実現

線形リストの実現(配列版)

実現方法



用意した配列の先頭からデータを順次格納
後続ノードへの着目は添え字をインクリメント
問題点


データを挿入・削除する際、配列内の要素ブロックを移動する必要あり
あらかじめ用意した配列の要素数以上のデータは格納できない
33
配列によるリストの実現
例) int a[MAXLENGTH];
a[0]
a[1]
last
最後の要素の
位置を記録
5
10
リスト
配列の大きさ
(定数)
MAXLENGTH 個
22
空
a[MAXLENGTH-1]
34
Insert(x, p, L)
位置pの後ろの要素を一つずつずらし,新しい要素のため
の場所を空けてから,要素を入れる

A
x
p
last
2
[0]
B
[1]
L
[2]
L
[3]
p
last
2
② 3
[0]
B
[1]
A ③
[2]
L
[3]
L
[4]
[4]
[5]
[5]
①
35
Delete(p, L)

last
4
削除する要素の後の要素を一つずつ前へずらし,すき間を
埋める
[0]
E
[1]
X
[2]
E
[3]
I
①
last
3
[4]
T
4
[5]
p
[0]
E
[1]
X
[2]
EI
[3]
TI
[4]
T
p
②
[5]
36
配列で実現する場合の注意点
last
2
[0]
E
[1]
I
[2]
T
p
[3]
[4]
[5]
[6]
削除する要素が存
在する範囲は[0]~
[last]
要素を挿入でき
る範囲は
[0]~[last+1]
lastがMAXLENGTH – 1の場合,リストは満杯
(これ以上,要素を挿入できない)
37
まとめ

多次元配列




構造体




2次元配列
3次元配列
応用例:年内の経過日数
typedef宣言
構造体のメンバーの参照
構造体の配列
配列によるリスト




スタックとキュー
リストの実現に使用できるデータ構造
リストを操作する代表的な関数8つ
配列による線形リストの実現
38
演習
#include <stdio.h>
#include <stdlib.h>
#define NUM 5
typedef struct { // 構造体の定義
float shincho;
// 身長
float taiju;
// 体重
} shintai;
void hyouji(shintai p[],int n)
// n個分のデータを表示する
{
int i;
for(i=0;i<n;i++)
printf("No.%2d 身長: %4.1f 体重: %4.1f \n",i,p[i].shincho,p[i].taiju);
}
39
演習
// メイン処理
void main()
{
int
i;
shintai p[NUM];
for(i=0;i<NUM;i++){
printf("No.%d\n",i);
printf("\t身長 : ");
scanf("%f",&p[i].shincho);
printf("\t体重 : "); scanf("%f",&p[i].taiju);
}
hyouji(p,NUM);
}
40
演習問題


入力人数を動的に変更できるようにプログラムを変更せよ。
但し:入力の数を標準入力で指定する
作成した cppファイルを印刷して提出してください。
名前と学籍番号をご記入のうえ、解答用紙(A4)を提出する
提出先:
工学部電子情報実験研究棟5階
NO.5506室のドアのポストに入れてください
締め切り:
来週月曜日(5月12日) 午前9時まで
41
ヒント
void main()
{
int i, num;
shintai *p;
printf("データ数: "); scanf("%d",&num);
作成箇所(1)ここでmalloc/ callocを使ってメモリを確保する.
shintai型でnum個分
for(i=0;i<num;i++){
printf("No.%d\n",i);
printf("\t身長 : ");
printf("\t体重 : ");
scanf("%f",&p[i].shincho);
scanf("%f",&p[i].taiju);
}
hyouji(p,num);
作成箇所(2)ここで確保したメモリをfreeを使って開放する.
}
42