コンピュータ基礎演習  ー探索、整列ー

98
ココココココココココ コココ コココ ココ ココ [email protected]

description

コンピュータ基礎演習  ー探索、整列ー. 岩井 儀雄 [email protected]. 線形探索と二分探索. 線形探索法 (linear search) データを最初から順番に探索する 例) {2, 4, 5, 8, 9, 11, 6, 7, 15, 20} から 15 を探す  最初の要素から始めて9回目→計算量 O(n) 二分探索法 (binary search) データをあらかじめソートしておき,中央の要素から検索する.. 二分探索 (binary search). - PowerPoint PPT Presentation

Transcript of コンピュータ基礎演習  ー探索、整列ー

コンピュータ基礎演習 ー探索、整列ー

岩井 儀雄[email protected]

線形探索と二分探索 線形探索法 (linear search)

データを最初から順番に探索する例) {2, 4, 5, 8, 9, 11, 6, 7, 15, 20} から 15 を探す 最初の要素から始めて9回目→計算量 O(n)

二分探索法 (binary search) データをあらかじめソートしておき,中央の要素から

検索する.

二分探索 (binary search) {2, 4, 5, 6, 7, 8, 9, 11, 15, 20} から 15を探す

{2, 4, 5, 6, 7, 8, 9, 11, 15, 20}1回目

二分探索 (binary search) {2, 4, 5, 6, 7, 8, 9, 11, 15, 20} から 15を探す

{2, 4, 5, 6, 7, 8, 9, 11, 15, 20}2回目

二分探索 (binary search) {2, 4, 5, 6, 7, 8, 9, 11, 15, 20} から 15を探す

{2, 4, 5, 6, 7, 8, 9, 11, 15, 20}

探索回数 3回計算量   log2(n) O(log n)

3回目

整列 (sort) 内部整列 (internal sort)

主記憶上で行う整列 外部整列 (external sort)

外部記憶装置(テープ等)上で行う

整列アルゴリズムの優劣→比較回数と交換回数の大小

安定な整列 (stable sort) 同じ値を持つデータ間の順序関係が整

列の前後で保たれている

例)整列前: 7 9 5A 4 8 5B 2  整列後: 2 4 5A 5B 7 8 9  (安定)  整列後: 2 4 5B 5A 7 8 9  (不安定)

単純な整列(バブルソート) 配列の後ろから先頭に向かって走査し,もし隣り合う二つ

の要素の大小関係が逆であったら入れ替える

整列前  20 6 55 74 3 45 13 87 46 301回目

比較

単純な整列(バブルソート) 配列の後ろから先頭に向かって走査し,もし隣り合う二つ

の要素の大小関係が逆であったら入れ替える

1回目  20 6 55 74 3 45 13 87 30 46

入替

単純な整列(バブルソート) 配列の後ろから先頭に向かって走査し,もし隣り合う二つ

の要素の大小関係が逆であったら入れ替える

1回目  20 6 55 74 3 45 13 87 30 46

比較

単純な整列(バブルソート) 配列の後ろから先頭に向かって走査し,もし隣り合う二つ

の要素の大小関係が逆であったら入れ替える

1回目  20 6 55 74 3 45 13 30 87 46

入替

単純な整列(バブルソート) 配列の後ろから先頭に向かって走査し,もし隣り合う二つ

の要素の大小関係が逆であったら入れ替える

1回目  20 6 55 74 3 45 13 30 87 46

比較

単純な整列(バブルソート) 配列の後ろから先頭に向かって走査し,もし隣り合う二つ

の要素の大小関係が逆であったら入れ替える

1回目  20 6 55 74 3 45 13 30 87 46

比較

単純な整列(バブルソート) 配列の後ろから先頭に向かって走査し,もし隣り合う二つ

の要素の大小関係が逆であったら入れ替える

1回目  20 6 55 74 3 13 45 30 87 46

入替

単純な整列(バブルソート) 配列の後ろから先頭に向かって走査し,もし隣り合う二つ

の要素の大小関係が逆であったら入れ替える

1回目  20 6 55 74 3 13 45 30 87 46

比較

単純な整列(バブルソート) 配列の後ろから先頭に向かって走査し,もし隣り合う二つ

の要素の大小関係が逆であったら入れ替える

1回目  20 6 55 74 3 13 45 30 87 46     20 6 55 3 74 13 45 30 87 46 20 6 3 55 74 13 45 30 87 46 20 3 6 55 74 13 45 30 87 46 3 20 6 55 74 13 45 30 87 46

単純な整列(バブルソート) 配列の後ろから先頭に向かって走査し,もし隣り合う二つ

の要素の大小関係が逆であったら入れ替える

2回目  3 20 6 55 74 13 45 30 87 46     46 87 30 46 30 45 13 30 13 74 13 55 6 13 6 20

単純な整列(バブルソート) 配列の後ろから先頭に向かって走査し,もし隣り合う二つ

の要素の大小関係が逆であったら入れ替える

2回目  3 6 20 13 55 74 30 45 46 873回目 3 6 13 20 30 55 74 45 46 874回目 3 6 13 20 30 45 55 74 46 875回目 3 6 13 20 30 45 46 55 74 876回目 3 6 13 20 30 45 46 55 74 877回目 3 6 13 20 30 45 46 55 74 878回目  3 6 13 20 30 45 46 55 74 879回目  3 6 13 20 30 45 46 55 74 87

バブルソート(疑似コード)

for (i←0..n-1) for (j←n-1..i) if !(a[j-1] ≦ a[j]) swap(a[j-1],a[j])

整列アルゴリズムが満たす事後条件 i,j[i < j] a[i] a[ j]

計算量は O(n2)

右辺値の交換

バブルソート(C言語)

for (i←0..n-1) for (j←n-1..i) if !(a[j-1] ≦ a[j]) swap(a[j-1],a[j])

void bubble_sort(int a[],int n) { int I,j,t; for (int I=0; I<n-1; ++I) for (int j=n-1; j>I; --j) if (a[j-1] > a[j]) { t = a[j]; a[j] = a[j-1]; a[j-1] = t; }}

swap(a[j-1],a[j]) に相当する

単純な整列(選択ソート) 未整列部分から最小の要素を選び出し,それを未整

列部分の先頭と入れ替える

整列アルゴリズムが満たす事後条件 i,j[i < j] a[i] a[ j]

a[0] a[1] a[i –1] a[i] a[n –1]整列済 未整列部分

未整列部分から最小値を取り出し続けると下記の条件を満たす

選択ソート

整列前  20 6 55 74 3 45 13 87 46 301回目

入替

選択ソート

整列前  20 6 55 74 3 45 13 87 46 301回目 3 6 55 74 20 45 13 87 46 30

選択ソート

整列前  20 6 55 74 3 45 13 87 46 301回目 3 6 55 74 20 45 13 87 46 302回目 3 6 55 74 20 45 13 87 46 30

入替

選択ソート

整列前  20 6 55 74 3 45 13 87 46 301回目 3 6 55 74 20 45 13 87 46 302回目 3 6 55 74 20 45 13 87 46 303回目  3 6 13 74 20 45 55 87 46 304回目  3 6 13 20 74 45 55 87 46 305回目  3 6 13 20 30 45 55 87 46 746回目  3 6 13 20 30 45 55 87 46 747回目  3 6 13 20 30 45 46 87 55 748回目  3 6 13 20 30 45 46 55 87 749回目  3 6 13 20 30 45 46 55 74 87

選択ソート(疑似コード)

for (i←0..n-2) lowest←argmin(a[j]) i<j<n swap(a[i],a[lowest])

選択ソート(C言語)

void selection_sort(int a[],int n) { int i,j,t,lowest,lowval; for (i = 0; i<n-1; ++i) { lowest = i; lowval = a[i]; for (j = i+1; j < n; ++j) if (a[j] < lowval) { lowval = a[j]; lowest = j; } t = a[I]; a[I] = a[lowest]; a[lowest] = t; }}

argmin の計算部分

選択ソートの計算量 ループ n 回, argmin の計算量 O(n) 選択ソートの計算量は O(n2)

単純な整列(挿入ソート) 配列の一部分が整列済みの時に,残りの要素

を一つずつ整列済みの中に挿入する整列前  20 6 55 74 3 45 13 87 46 301回目  6 20 55 74 3 45 13 87 46 302回目 6 20 55 74 3 45 13 87 46 303回目  6 20 55 74 3 45 13 87 46 304回目  3 6 20 55 74 45 13 87 46 305回目  3 6 20 45 55 74 13 87 46 306回目  3 6 13 20 45 55 74 87 46 307回目  3 6 13 20 45 55 74 87 46 308回目  3 6 13 20 45 46 55 74 87 309回目  3 6 13 20 30 45 46 55 74 87

挿入ソート(疑似コード)

for (i←0..n-1) j←i while (! (a[j-1] ≦ a[j]) ) swap( a[j-1], a[j] ) j←j-1

計算量:外側ループ O(n) 内側ループ O(n)合計:  O(n2)

コンピュータ基礎演習 ー計算量ー

岩井 儀雄[email protected]

計算量 (complexity) 時間計算量 (time complexity)

アルゴリズムがデータに対してどれくらい時間がかかるかを示す

空間計算量 (space complexity) アルゴリズムがデータに対してどれくらい記憶領域を必要

とするかを示す

時間計算量と空間計算量はトレードオフの関係にあることが多い

計算機にとって記憶資源は潤沢にあるので,通常時間計算量の方が重きを置かれることが多い

オーダー記法O 計算量 T(n) の上界値を評価するとき, O(f

(n)) という記法を用い,オーダー f(n) と読む.

ある正定数 c と n0 が存在して, n0以上の n に対して,常に T(n) ≦cf(n) が成立するという意味n0 の役割は有限個の例外を許すことにある.

c,n0 c > 0, n n0 T(n) cf (n)

オーダー記法 Ω 計算量の下界値のオーダーを表すには,

記法 Ωを用いる. T(n)=Ω(f(n)) とは,

cn T(n) cf (n)

計算量とオーダ表記の関係

n

計算量

計算量の下界Ω

計算量の上界 O実際の計算量

有限個の例外は OK

n0

オーダーの演算 T1 = O( f (n)), T2 = O(g(n))

T1 + T2 = O(max( f (n),g(n)))T1 T2 = O( f (n) g(n))

例) T1 = O(n2), T2 = O(n3)T1 + T2 = O(n3)T1 T2 = O(n5)

最悪計算量と平均計算量 同じアルゴリズムでも入力するデータに応じ

て計算量が変化する.そこで,客観的に測定し評価する必要がある.

最悪計算量 worst case complexity 全ての入力パターンに対して最大の計算量を要す

るものに基づいて定める 平均計算量 average complexity

全ての入力パターンとその入力の生起確率に基づいて計算量の平均を求める

コンピュータ基礎演習 ー再帰ー

岩井 儀雄[email protected]

再帰 再帰的 (recursive) な構造とは,自分自身 (n次 ) を定義するのに,自分自身より 1次低い集合 (n-1次 ) を用い,さらにその部分集合は,より低次の部分集合を用いて定義するということを繰り返す構造.このような構造を一般的に再帰 (recursion) と呼ぶ.

再帰 数学ではいろいろな概念を再帰的に定義している.

a1,a2,例)数列 a1 = 1

an = an – 1 + 2n – 1

再帰呼び出し 関数の中で自分自身を関数として呼び

出すこと

int A(int n) { if (n == 1) return 1; /* 再帰呼び出しの停止条件 */ else return A(n-1)+2*n-1;}

a1,a2,例)数列 a1 = 1

an = an – 1 + 2n – 1

再帰呼び出し例 階乗 (n!) の計算プログラム

f0 = 1fn = n fn – 1

#include <stdio.h>int factorial(int n) { if (n == 0) return 1; else return n*factorial(n-1);}int main() { int n; for (n=0; n<10; ++n) printf( “ %2d! = %d\n”, n, factorial(n) ); return 0;}

階乗 (n!) の計算実行例 0! = 1 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880

10! = 3628800 11! = 39916800 12! = 479001600 13! = 1932053504 14! = 1278945280 15! = 2004310016 16! = 2004189184 17! = -288522240 18! = -898433024 19! = 109641728

実行: PowerBook G4, MacOS X 10.2.5, gcc 3.1

演算結果が正しくない13! = 622702080014! = 8717829120015! = 130767436800016! = 2092278988800017! = 355568742809600018! = 6402373705728000019! = ?

int が 32ビットなら,正の数は231 = 2147483648までしか表現できない

演習課題 フィボナッチ数列を再帰を用いて求める

f1 = f2 = 1fn = fn – 1 + fn – 2

#include <stdio.h>int fibonacci(int n) { if (n == 1 || n == 2) return 1; else return fibonacci(n-1)+fibonacci(n-2);}int main() { int n; for (n=0; n<20; ++n) printf( “ %2d! = %d\n”, n, fibonacci(n) ); return 0;}

コンピュータ基礎演習 ーリスト構造ー

岩井 儀雄[email protected]

ADT Stack (スタック ) データの集まり 一番上の要素しか操作できない イメージ的には物を上に積んだ状態

スタックに可能な操作・一番上の要素の値を見る (top)・一番上の要素を取り除く (pop)・上に要素を積む (push)・スタックが空? (isEmpty)・スタックの要素数 (size)

ADT Queue(待ち行列) データの集まり 要素の挿入,削除が端でしかできない イメージ的には人の待ち行列

待ち行列の先頭

ADT Stack, ADT Queue の実装 前回は配列を使って実装した 今回はリスト構造を使って実装する

→まずはリスト構造について

リスト構造 連結リスト 循環リスト 双方向リスト

連結リスト (Linked list) データの集まり データが一覧表(リスト)のように連なっている構造 連なりの表現には効率のため,ポインタが用いられることが多い もちろん,配列とインデックスでも実装できる(リスト容量が制限)

データ データ データ

DATA[0] DATA[1] DATA[2] … DATA[N]

データ 未使用 データ … データ

IDX[0] IDX[1] IDX[2] … IDX[N]

2 -1 N … EOD

連結リストの操作 要素の挿入 (insert) 要素の削除 (erase) リストの連結( concatenate ) リストの分断 (split) 要素の探索 (find) 空リストの生成 (create) 要素数 (size) 先頭要素を見る (front) 最後尾要素を見る (tail)

連結リストの操作 要素の挿入 (insert)

指定された要素の次の位置に要素を挿入する

A C D

B

連結リストの操作 要素の挿入 (insert)

指定された要素の次の位置に要素を挿入する

A C D

B

連結リストの操作 要素の削除 (erase)

指定された要素を削除する

A C D

B

連結リストの操作 要素の削除 (erase)

指定された要素を削除する

A D

B

連結リストの操作 リストの連結 (concatenate)

指定された要素の次の位置にリストを連結する

A E F

B D

連結リストの操作 リストの連結 (concatenate)

指定された要素の次の位置にリストを連結する

A E F

B D

連結リストの操作 リストの分断 (split)

指定された要素の次の位置からリストを分断する

A E F

B D

連結リストの操作 リストの分断 (split)

指定された要素の次の位置からリストを分断する

A E F

B D

連結リストの操作 要素の探索 (find)

指定された値を持つ要素の位置を返す

A B D

B

連結リストの操作 要素の探索 (find)

指定された値を持つ要素の位置を返す

A B D

B

連結リストの操作 空リストの生成 (create)

要素のないリスト なぜ必要か?

要素をすべて削除したり,分断で空になったりする どう表す?

ヘッダを付加して表現 ヘッダのみの場合は空リストとして扱う

A B D

header

連結リストの操作 空リストの生成 (create)

要素のないリスト なぜ必要か?

要素をすべて削除したり,分断で空になったりする どう表す?

ヘッダを付加して表現 ヘッダのみの場合は空リストとして扱う

A B D

header

構造体とポインタを用いた連結リストの実装

構造体を利用する利点 データと次のデータへのポインタが同時に管理できる

ポインタを利用する利点 ヒープ領域を利用して動的にメモリを使用できる 固定のサイズに依存しない 要素の位置指定に利用できる

struct CELL { struct CELL *next; /* 次のデータへのポインタ */ DATATYPE data; /* 格納されるデータ */};

構造体とポインタを用いた連結リストの実装 空リストの生成 (create)

ヘッダを作成して初期化する

struct CELL myList; /* List を表すヘッダ変数 */myList.next = NULL; /* 空を示す */myList.data = 0; /* header の data は使用されないが,        一応 int 型として話を進める */

リストの生成はよく使う&要素の生成にも利用できるので関数にする

構造体とポインタを用いた連結リストの実装 空リスト ( 要素 ) の生成 (create)

ヘッダを作成して初期化する 作成したヘッダをポインタとして返す

struct CELL *create(DATATYPE data) { struct CELL *p = (struct CELL*)malloc(sizeof(struct CELL)); p->next = NULL; p->data = data; return p;}

struct CELL の格納領域をヒープ領域に確保する

空リスト(要素)生成の実装(C言語)

struct CELL *create(DATATYPE data) { struct CELL *p = (struct CELL*)malloc(sizeof(struct CELL)); p->next = NULL; p->data = data; return p;}

struct CELL の格納領域をヒープ領域に確保する

間違ったコーディング例)struct CELL *create(DATATYPE data) { struct CELL p; /* 局所変数 */ p.next = NULL; p.data = data; return &p; /* 関数からでた途端に局所変数は有効でなくなる */}

要素挿入の実装 指定された要素の次の位置に挿入する

要素の指定にはポインタを利用する 挿入する要素もポインタで指定する

void insert( struct CELL *pos, struct CELL *val ) { val->next = pos->next; pos->next = val;}

BA

posval

C

要素挿入の実装 指定された要素の次の位置に挿入する

要素の指定にはポインタを利用する 挿入する要素もポインタで指定する

void insert( struct CELL *pos, struct CELL *val ) { val->next = pos->next; pos->next = val;}

BA

posval

C

要素挿入の実装 指定された要素の次の位置に挿入する

要素の指定にはポインタを利用する 挿入する要素もポインタで指定する

void insert( struct CELL *pos, struct CELL *val ) { val->next = pos->next; pos->next = val;}

BA

posval

C

要素削除の実装 指定された要素の次を削除する

要素の指定にはポインタを利用する 削除された要素のポインタを返す

struct CELL *erase( struct CELL *pos ) { struct CELL *p = pos->next; if (p != NULL) { pos->next = p->next; p->next = NULL; } return p;}

BA

pos

C

要素削除の実装 指定された要素の次を削除する

要素の指定にはポインタを利用する 削除された要素のポインタを返す

struct CELL *erase( struct CELL *pos ) { struct CELL *p = pos->next; if (p != NULL) { pos->next = p->next; p->next = NULL; } return p;}

BA

pos

C

p

要素削除の実装 指定された要素の次を削除する

要素の指定にはポインタを利用する 削除された要素のポインタを返す

struct CELL *erase( struct CELL *pos ) { struct CELL *p = pos->next; if (p != NULL) { pos->next = p->next; p->next = NULL; } return p;}

BA

pos

C

p

最後尾要素を見る (tail)

A B

header

リストの最後尾を返す

tail の実装

A B

header

リストの最後尾を返す

struct CELL *tail( struct CELL *pos ) { while (pos->next != NULL) pos = pos->next; return pos;}

pos

tail の実装

A B

header

リストの最後尾を返す

struct CELL *tail( struct CELL *pos ) { while (pos->next != NULL) pos = pos->next; return pos;}

pos

tail の実装

A B

header

リストの最後尾を返す

struct CELL *tail( struct CELL *pos ) { while (pos->next != NULL) pos = pos->next; return pos;}

pos

tail の実装

A B

header

リストの最後尾を返す

struct CELL *tail( struct CELL *pos ) { while (pos->next != NULL) pos = pos->next; return pos;}

リスト連結の実装 二つのリストのヘッダを渡す 連結されたヘッダを返す

A B

D

header

C

struct CELL *concatenate( struct CELL *L1, struct CELL *L2 );

リスト連結の実装 二つのリストのヘッダを渡す 連結されたヘッダを返す

A B

D

header

C

struct CELL *concatenate( struct CELL *L1, struct CELL *L2 ) {struct CELL *p = tail( L1 );insert( p, L2->next ); L2->next = NULL;return L1;

};

L1

L2

リスト連結の実装 二つのリストのヘッダを渡す 連結されたヘッダを返す

A B

D

header

C

struct CELL *concatenate( struct CELL *L1, struct CELL *L2 ) {struct CELL *p = tail( L1 );insert( p, L2->next ); L2->next = NULL;return L1;

};

L1

L2

p

リスト連結の実装 二つのリストのヘッダを渡す 連結されたヘッダを返す

A B

D

header

C

struct CELL *concatenate( struct CELL *L1, struct CELL *L2 ) {struct CELL *p = tail( L1 );insert( p, L2->next ); L2->next = NULL;return L1;

};

L1

L2

p

リスト連結の実装 二つのリストのヘッダを渡す 連結されたヘッダを返す

A B

D

header

C

struct CELL *concatenate( struct CELL *L1, struct CELL *L2 ) {struct CELL *p = tail( L1 );insert( p, L2->next ); L2->next = NULL;return L1;

};

L1

L2

p

要素探索の実装 要素を比較し等価な要素を探索 最初に見つかった要素の位置を返す なければ NULL を返す

struct CELL *find( struct CELL *p, DATATYPE val );

A B

headerp

C

要素探索の実装 要素を比較し等価な要素を探索 最初に見つかった要素の位置を返す なければ NULL を返す

struct CELL *find( struct CELL *p, DATATYPE val ) {p = p->next;while (p != NULL)

if (isEqual(p.data, val)) return p; else p = p->next;return p;

}

A B

headerp

C

要素探索の実装 要素を比較し等価な要素を探索 最初に見つかった要素の位置を返す なければ NULL を返す

struct CELL *find( struct CELL *p, DATATYPE val ) {p = p->next;while (p != NULL)

if (isEqual(p.data, val)) return p; else p = p->next;return p;

}

A B

headerp

C

要素探索の実装 要素を比較し等価な要素を探索 最初に見つかった要素の位置を返す なければ NULL を返す

struct CELL *find( struct CELL *p, DATATYPE val ) {p = p->next;while (p != NULL)

if (isEqual(p.data, val)) return p; else p = p->next;return p;

}

A B

headerp

C

要素探索の実装 要素を比較し等価な要素を探索 最初に見つかった要素の位置を返す なければ NULL を返す

struct CELL *find( struct CELL *p, DATATYPE val ) {p = p->next;while (p != NULL)

if (isEqual(p.data, val)) return p; else p = p->next;return p;

}

A B

headerp

C

要素探索の実装 要素を比較し等価な要素を探索 最初に見つかった要素の位置を返す なければ NULL を返す

struct CELL *find( struct CELL *p, DATATYPE val ) {p = p->next;while (p != NULL)

if (isEqual(p.data, val)) return p; else p = p->next;return p;

}

A B

headerp

C

要素探索の実装 要素を比較し等価な要素を探索 最初に見つかった要素の位置を返す なければ NULL を返す

struct CELL *find( struct CELL *p, DATATYPE val ) {p = p->next;while (p != NULL)

if (isEqual(p.data, val)) return p; else p = p->next;return p;

}

A B

headerp

C

要素探索の実装 要素を比較し等価な要素を探索 最初に見つかった要素の位置を返す なければ NULL を返す

struct CELL *find( struct CELL *p, DATATYPE val ) {p = p->next;while (p != NULL)

if (isEqual(p.data, val)) return p; else p = p->next;return p;

}

A B

headerp

C

要素探索の実装 要素を比較し等価な要素を探索 最初に見つかった要素の位置を返す なければ NULL を返す

struct CELL *find( struct CELL *p, DATATYPE val ) {p = p->next;while (p != NULL)

if (isEqual(p.data, val)) return p; else p = p->next;return p;

}

A B

headerp

C

要素探索の実装 要素を比較し等価な要素を探索 最初に見つかった要素の位置を返す なければ NULL を返す

struct CELL *find( struct CELL *p, DATATYPE val ) {p = p->next;while (p != NULL)

if (isEqual(p.data, val)) return p; else p = p->next;return p;

}

A B

headerp

C

等価判定関数 isEqual の実装 等価判定

二つのデータが等しいかどうか 演算子 = は基本データ型にしか利用できない

DATATYPE の中身で等価判定関数は異なる(実装,設計により異なる)

等価とはどういうことか定義が必要

int isEqual(DATATYPE d1, DATATYPE d2); d1 と d2 が等価ならば !0 そうでないならば 0 を返す

循環リスト

A B

header

C

連結リスト linked list

循環リスト circular list

A B

header

C

後ろに移動し続ければ,先頭に戻れる

循環リストの特徴と目的 環状に並んだデータ構造を表現 要素を結び合わせるポインタをたどれば,循環リストに含まれる全要素を順番に処理できる

厳密には「最初」と「最後」の要素というものは存在しない

循環リストの操作 要素の挿入・削除

連結リストの場合と同様 要素の追跡 (trace)

基本的に連結リストと同様 最後尾が NULLでは判定できないので工夫

が必要→追跡を開始した要素に到達したら終了

要素追跡の実装例

struct CELL *p, *ptr; /* 追跡用のポインタ変数 */

if (ptr != NULL) { /* 追跡開始点 ptr */p = ptr; /* 追跡開始点を作業用変数 p にコピー */do {

/* p が指すセルの処理 */p = p->next;

} while (p != ptr); /* 開始点まで戻ってきたら終了 */}

変数 ptr が NULL なら,この循環リストは空(要素なし)なので何もしない.リストが空でないなら,セルを順番にたどって処理するDo .. While 形式によりループ末尾で終了条件を判断する