はじめての C
「リンクリスト 4」
次に 入る前に、大急ぎで、線形リストと 双方向リストについて ノートしてみましょう。
線形リストに 項目を 挿入するとき、2つの リンク つまり 双方の next ポインタを 書き換える 必要が あります。
n->next = item->next; /* n の next ポインタに セット */
item->next = n; /* item の next ポインタに セット */しかし 双方向リストの 場合は、リンクの数を 2倍 用意する 必要が でてきます。
双方向リストでは、仕事が 2倍に なることを 覚えておくと、助かることに なります。
n->prev = item; /* n の prev ポインタ */
n->next = item->next; /* n の next ポインタ */
n->next->prev = n; /* n の next->prev ポインタ */
n->prev->next = n; /* n の prev->next ポインタ */準備された これらの 4つの リンクだけで、項目は 正しく 挿入されるのです。
ただし、リンクの中に 番兵が あると、問題が 起こってきます。
そこで、双方向リストの どこであっても、正しく ノードを 挿入する コードを 書いてみましょう。
n->prev = item;
すべての リンクリストで、項目を 挿入 あるいは 削除することが できます。
n->next = item->next;
if (n->prev != NULL)
n->prev->next = n;
if (n->next != NULL)
n->next->prev = n;実際、この tutorial のために 書いた ごく小さな リストライブラリでは、関数が 4つ 含まれているだけです。 つまり、挿入と 削除、それに 先頭項目の 検出と 末尾項目の 検出です。
そのヘッダは こうなっています ▽
#ifndef PRELIST_H
#define PRELIST_H
typedef enum {BEFORE, AFTER} Direction;
typedef struct element *Element;
typedef struct element *List;
struct element {
Element prev;
Element next;
int data;
};
Element first(Element iter);
Element last(Element iter);
List add(List list, Element iter, Element item, Direction dir);
Lisr rem(List list, Element iter);
#endif
(注 1) #ifndef は、defined 演算子を 使った 条件文の マクロを 参照。
http://d.hatena.ne.jp/sekiyo/20050221
また #ifndef で 定義した NAME は ヘッダファイル自体を 指すので、上の例だと、PRELIST_H は ヘッダファイル名が prelist.h であることを (おそらく) 表している。
typedef と ともに 用いる 場合が ある。
typedef enum {定数0, 定数1, ... } 列挙子名;
シンプルでしょ ?
先頭と 末尾の ポインタを 保守 - maintain - している コードでは、first と last の 関数は、まず 絶対 使うことは ありません !
ここに それを 付け加えた 理由は ただ 1つ、たいていの 場合 - 私もですが - 先頭と 末尾の 保守を ナマケてしまうからです。
しかし、この 先頭と 末尾を 普段から 使うことで、大きな リストに おいても、大きな パフォーマンスを ヒットさせる - be a big performance hit - ことが できるという、課題を 知るように なります。
でも、この tutorial では、リストは 小さいでしょうから、パフォーマンスの 問題は 受け入れられる 範囲に なっています。
On to the code !