スポンサーリンク
連結リスト(片方向)でpushとpopをc言語で実装
連結リスト(片方向)でpushとpopをc言語で実装してみました。
下記の記事で、配列をスタック構造としてpush/popを実装しています。
参考:スタックのpush,pop関数をC言語とPythonで実装
まずは、片方向リストを構造体で定義します。
リストの構造体には、先頭のノードのポインタをメンバ変数に定義します。
ノードの先頭がわかるようになるので、実装が楽になりました。
typedef struct node{ int data; struct node* pNextNode; }node_t; typedef struct list{ node_t* pTopNode; }list_t;
ノードをmalloc()で確保
続いて、下記は初期化関数です。
最初のノードをmalloc()で確保します。
ノードのpNextNodeにはNULLを代入します。
関数の引数に構造体を渡す場合の注意点を、下記の記事で書いています。
参考:[c言語]関数の引数に構造体を渡す場合の注意(サンプルコードあり)
node_t* initNode(int data){ node_t* pNode = NULL; pNode = (node_t*)malloc(sizeof(node_t)); if(pNode == NULL){ printf("malloc error\n"); return NULL; } pNode->data = data; pNode->pNextNode = NULL; return pNode; } list_t* initList(int data){ list_t* pList = NULL; pList = (list_t*)malloc(sizeof(list_t)); if(pList == NULL){ printf("init list malloc error\n"); return NULL; } pList->pTopNode = initNode(data); if(pList->pTopNode == NULL){ printf("init node error\n"); free(pList); return NULL; } printfList(pList); return pList; }
スポンサーリンク
push関数
push関数になります。
while()で終端ノードを探索します。
新規で作成したノードのポインタを、終端ノードのpNextNodeに格納します。
void push(list_t* pList, int data){ node_t* pNode = NULL; if(pList == NULL){ printf("NULL chk error\n"); return; } //リストが空の場合はノードを生成 if(pList->pTopNode == NULL){ pList->pTopNode = initNode(data); if(pList->pTopNode == NULL){ printf("push error\n"); } printfList(pList); return; } pNode = pList->pTopNode; while(pNode->pNextNode != NULL){ pNode = pNode->pNextNode; }; pNode->pNextNode = initNode(data); if(pNode->pNextNode == NULL){ printf("push erro\n"); return; } printfList(pList); return; }
pop関数
pop関数になります。
while()で終端ノードを探索します。
終端ノードをfree()して、終端ノードの一つ前のpNextNodeにNULLを格納します。
void pop(list_t* pList){ node_t* pPreNode = NULL; node_t* pNode = NULL; if(pList == NULL){ printf("NULL chk error\n"); return; } if(pList->pTopNode == NULL){ printf("list empty\n"); return; } pPreNode = pList->pTopNode; pNode = pList->pTopNode; while(pNode->pNextNode != NULL){ pPreNode = pNode; pNode = pNode->pNextNode; } pPreNode->pNextNode = NULL; //リストが空になった場合の処理 if(pNode == pList->pTopNode){ pList->pTopNode = NULL; free(pNode); return; } free(pNode); printfList(pList); return; }
指定した値を持つノードを削除
指定した値を持つノードを削除する関数です。
連結リストの各ノードをループ処理しながら、指定した値を持つノードを探索します。
探索で位置したノードがあれば、そのノードをfree()して、前後のノードを連結し直します。
pPreNode, pTmpNode, pNodeを3つの変数を用いているところがコーディングのポイントですね。
void deleteNodeData(list_t* pList, int data){ node_t* pNode = NULL; node_t* pPreNode = NULL; node_t* pTmpNode = NULL; if(pList == NULL){ printf("NULL chk error\n"); return; } if(pList->pTopNode == NULL){ printf("list empty\n"); return; } pNode = pList->pTopNode; pPreNode = pNode; while(pNode != NULL){ if(pNode->data == data){ //ノードの張替え if(pNode == pList->pTopNode){ pList->pTopNode = pNode->pNextNode; }else{ pPreNode->pNextNode = pNode->pNextNode; } pTmpNode = pNode; pNode = pNode->pNextNode; free(pTmpNode); }else{ pPreNode = pNode; pNode = pNode->pNextNode; } } printfList(pList); return; }
重複したノードを削除
続いて、重複した値を持つノードを削除して、ユニークなノードのみにする関数です。
ノードのポインタを2つ用意して、2重にループ処理して各ノード同士の値を比較していきます。
比較して一致した場合には、片方向のノードをfree()して、前後のノードを再連結していきます。
void deleteDupNode(list_t* pList){ node_t* pNode_1 = NULL; node_t* pNode_2 = NULL; node_t* pPreNode_2 = NULL; node_t* pTmpNode = NULL; if(pList == NULL){ printf("NULL chk error\n"); return; } if(pList->pTopNode == NULL){ printf("list empty\n"); return; } pNode_1 = pList->pTopNode; pPreNode_2 = pNode_1; pNode_2 = pNode_1->pNextNode; while(pNode_1->pNextNode != NULL){ while(pNode_2 != NULL){ if(pNode_1->data == pNode_2->data){ pPreNode_2->pNextNode = pNode_2->pNextNode; pTmpNode = pNode_2; pNode_2 = pNode_2->pNextNode; free(pTmpNode); }else{ pPreNode_2 = pNode_2; pNode_2 = pNode_2->pNextNode; } } pNode_1 = pNode_1->pNextNode; pPreNode_2 = pNode_1; //最終ノードが削除された場合は、pNode_1=NULLとなる //pNode_1->pNextNodeでfaultするのを回避 if(pNode_1 == NULL){ break; }else{ pNode_2 = pNode_1->pNextNode; } } printfList(pList); return; }
リストをfree()してメモリ解放
構造体のメンバ変数を参照するサンプルコードを下記の記事で書いています。
参考:[c言語]構造体のメンバ変数を参照するサンプルコード
最後に、リストをfree()してメモリ解放する関数になります。
各ノードを先頭から解放して、最後にリストを解放します。
void deleteList(list_t* pList){ node_t* pPreNode = NULL; node_t* pNode = NULL; if(pList == NULL){ printf("NULL chk error\n"); return; } pPreNode = pList->pTopNode; pNode = pList->pTopNode; while(pNode != NULL){ pPreNode = pNode; pNode = pNode->pNextNode; free(pPreNode); } free(pList); return; }
スポンサーリンク
サンプルコード
下記がサンプルコードになります。
main()関数では、動作確認も兼ねていたので、push/popを複数回繰り返しています。
呼び出し元でのエラーチェックは今回は行っていません。
$ cat sample.c #include <stdio.h> #include <stdlib.h> typedef struct node{ int data; struct node* pNextNode; }node_t; typedef struct list{ node_t* pTopNode; }list_t; void printfList(list_t* pList){ node_t* pNode = pList->pTopNode; if(pList == NULL){ printf("NULL chk error\n"); return; } while(pNode != NULL){ printf("%d ", pNode->data); pNode = pNode->pNextNode; }; printf("\n"); return; } node_t* initNode(int data){ node_t* pNode = NULL; pNode = (node_t*)malloc(sizeof(node_t)); if(pNode == NULL){ printf("malloc error\n"); return NULL; } pNode->data = data; pNode->pNextNode = NULL; return pNode; } void push(list_t* pList, int data){ node_t* pNode = NULL; if(pList == NULL){ printf("NULL chk error\n"); return; } //リストが空の場合はノードを生成 if(pList->pTopNode == NULL){ pList->pTopNode = initNode(data); if(pList->pTopNode == NULL){ printf("push error\n"); } printfList(pList); return; } pNode = pList->pTopNode; while(pNode->pNextNode != NULL){ pNode = pNode->pNextNode; }; pNode->pNextNode = initNode(data); if(pNode->pNextNode == NULL){ printf("push erro\n"); return; } printfList(pList); return; } void pop(list_t* pList){ node_t* pPreNode = NULL; node_t* pNode = NULL; if(pList == NULL){ printf("NULL chk error\n"); return; } if(pList->pTopNode == NULL){ printf("list empty\n"); return; } pPreNode = pList->pTopNode; pNode = pList->pTopNode; while(pNode->pNextNode != NULL){ pPreNode = pNode; pNode = pNode->pNextNode; } pPreNode->pNextNode = NULL; //リストが空になった場合の処理 if(pNode == pList->pTopNode){ pList->pTopNode = NULL; free(pNode); return; } free(pNode); printfList(pList); return; } list_t* initList(int data){ list_t* pList = NULL; pList = (list_t*)malloc(sizeof(list_t)); if(pList == NULL){ printf("init list malloc error\n"); return NULL; } pList->pTopNode = initNode(data); if(pList->pTopNode == NULL){ printf("init node error\n"); free(pList); return NULL; } printfList(pList); return pList; } void deleteList(list_t* pList){ node_t* pPreNode = NULL; node_t* pNode = NULL; if(pList == NULL){ printf("NULL chk error\n"); return; } pPreNode = pList->pTopNode; pNode = pList->pTopNode; while(pNode != NULL){ pPreNode = pNode; pNode = pNode->pNextNode; free(pPreNode); } free(pList); return; } void deleteNodeData(list_t* pList, int data){ node_t* pNode = NULL; node_t* pPreNode = NULL; node_t* pTmpNode = NULL; if(pList == NULL){ printf("NULL chk error\n"); return; } if(pList->pTopNode == NULL){ printf("list empty\n"); return; } pNode = pList->pTopNode; pPreNode = pNode; while(pNode != NULL){ if(pNode->data == data){ //ノードの張替え if(pNode == pList->pTopNode){ pList->pTopNode = pNode->pNextNode; }else{ pPreNode->pNextNode = pNode->pNextNode; } pTmpNode = pNode; pNode = pNode->pNextNode; free(pTmpNode); }else{ pPreNode = pNode; pNode = pNode->pNextNode; } } printfList(pList); return; } void deleteDupNode(list_t* pList){ node_t* pNode_1 = NULL; node_t* pNode_2 = NULL; node_t* pPreNode_2 = NULL; node_t* pTmpNode = NULL; if(pList == NULL){ printf("NULL chk error\n"); return; } if(pList->pTopNode == NULL){ printf("list empty\n"); return; } pNode_1 = pList->pTopNode; pPreNode_2 = pNode_1; pNode_2 = pNode_1->pNextNode; while(pNode_1->pNextNode != NULL){ while(pNode_2 != NULL){ if(pNode_1->data == pNode_2->data){ pPreNode_2->pNextNode = pNode_2->pNextNode; pTmpNode = pNode_2; pNode_2 = pNode_2->pNextNode; free(pTmpNode); }else{ pPreNode_2 = pNode_2; pNode_2 = pNode_2->pNextNode; } } pNode_1 = pNode_1->pNextNode; pPreNode_2 = pNode_1; //最終ノードが削除された場合は、pNode_1=NULLとなる //pNode_1->pNextNodeでfaultするのを回避 if(pNode_1 == NULL){ break; }else{ pNode_2 = pNode_1->pNextNode; } } printfList(pList); return; } int main(){ list_t* pList = NULL; pList = initList(1); push(pList,2); push(pList,3); pop(pList); push(pList,3); pop(pList); pop(pList); pop(pList); pop(pList); push(pList,1); push(pList,2); pop(pList); pop(pList); pop(pList); push(pList,1); push(pList,2); push(pList,3); deleteList(pList); pList = initList(1); push(pList,2); pop(pList); pop(pList); pop(pList); deleteList(pList); pList = initList(1); push(pList,1); push(pList,1); deleteDupNode(pList); pop(pList); pop(pList); pop(pList); push(pList,1); push(pList,1); push(pList,2); push(pList,2); push(pList,3); push(pList,3); deleteDupNode(pList); deleteList(pList); pList = initList(1); push(pList,1); push(pList,2); push(pList,3); push(pList,1); push(pList,2); push(pList,1); push(pList,1); deleteNodeData(pList, 1); deleteList(pList); return 0; }
下記が実行結果になります。
$ gcc -o sample sample.c $ ./sample 1 1 2 1 2 3 1 2 1 2 3 1 2 1 list empty 1 1 2 1 list empty 1 1 2 1 2 3 1 1 2 1 list empty 1 1 1 1 1 1 1 list empty list empty 1 1 1 1 1 2 1 1 2 2 1 1 2 2 3 1 1 2 2 3 3 1 2 3 1 1 1 1 1 2 1 1 2 3 1 1 2 3 1 1 1 2 3 1 2 1 1 2 3 1 2 1 1 1 2 3 1 2 1 1 2 3 2
スポンサーリンク