連結リスト(linked list、リンクリスト)とは、構造体のメンバに「次の構造体へのポインタ」を持たせて、数珠つなぎにしたデータ構造。いちばん最後の構造体の持つポインタは NULL。ようするに Scheme のリストと同じ構造だと思って良さそう。
#include #include typedef struct cell { int value; struct cell *next; } Cell; Cell *NewCell(int val); Cell *Cons(int val, Cell *list); Cell *Insert(Cell *list, int pos, int val); Cell *Delete(Cell *list, int pos); int Head(Cell **list); void DisplayList(Cell *list); void FreeList(Cell *list); int main(void) { Cell *list; int i, head; list = NewCell(0); for (i = 1; i < 10; i++) { list = Cons(i, list); } puts("before:"); DisplayList(list); list = Insert(list, 5, 10); puts("insert 10 at 5th position:"); DisplayList(list); list = Delete(list, 0); puts("delete 0th cell:"); DisplayList(list); head = Head(&list); printf("pick up head value (and delete the cell): %d\n", head); DisplayList(list); FreeList(list); return 0; } Cell *NewCell(int val) { Cell *data; data = (Cell *)malloc(sizeof(Cell)); data->value = val; data->next = NULL; return data; } Cell *Cons(int val, Cell *list) { Cell *data; data = NewCell(val); data->next = list; return data; } Cell *Insert(Cell *list, int pos, int val) { Cell *cell1, *cell2; int i = 1; if (pos == 0) { list = Cons(val, list); } else { cell1 = list; while (i < pos) { cell1 = cell1->next; i++; } cell2 = NewCell(val); cell2->next = cell1->next; cell1->next = cell2; } return list; } Cell *Delete(Cell *list, int pos) { Cell *cell1, *cell2; int i = 1; if (pos == 0) { cell2 = list; list = cell2->next; free(cell2); } else { cell1 = list; cell2 = cell1->next; while (i < pos) { cell1 = cell2; cell2 = cell1->next; i++; } cell1->next = cell2->next; free(cell2); } return list; } int Head(Cell **list) { int val; val = (*list)->value; *list = Delete(*list, 0); return val; } void DisplayList(Cell *list) { printf("%d", list->value); if (list->next == NULL) { printf("\n"); } else { printf(", "); DisplayList(list->next); } } void FreeList(Cell *list) { Cell *next = list->next; free(list); if (next != NULL) { FreeList(next); } }
Head() がちょっと難儀したので説明しておこう。
main() の中で list は連結リストの先頭へのポインタだ。Head() の呼び出しは Head(&list) というふうにしている。で、Head() の定義は、Head(Cell **list) となっていて **list はポインタへのポインタを表している。&list というのは list のアドレスを表しているので、この関数の引数は「連結リストの先頭のアドレスを保持する変数のアドレス」を渡していることになる。
なんでこういうことをしているかというと、Head() はリストの先頭の値を返すだけじゃなくて先頭のセルを削除する。ということはリストの先頭のアドレスが変わるってこと。ポインタの値を変更するには、こういうふうにポインタのポインタを渡して、関数内でアドレスを書き換えてやるわけだ。
ちなみに、Delete() もリストの先頭を削除するので、先頭へのポインタの値が変化するけど、こっちは素直に変更されたアドレスを返している(ほかに返す値がないから)。
実行結果:
takatoh@nightschool $ ./linkedlist before: 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 insert 10 at 5th position: 9, 8, 7, 6, 5, 10, 4, 3, 2, 1, 0 delete 0th cell: 8, 7, 6, 5, 10, 4, 3, 2, 1, 0 pick up head value (and delete the cell): 8 7, 6, 5, 10, 4, 3, 2, 1, 0