- ベストアンサー
双方向リストのプログラミングのチェック
指定したファイルを読み込み、一行ずつ構造体に保存し、表示させる。その後、キーボードから指定した行を削除し、処理後の結果を表示させる・・・といったプログラミングを作成しています。 通常の表示の部分はできましたが、後は指定した行の削除の部分ができません。 あらかじめHPで調べてあるので、削除するときは、その削除したい行をまたいで、前後のリストを繋げるといった概念的なのは大体理解できました。 自分なりに考えたり調べて、下のように打ちました。 ですが、キーボードから入力した値をどう関数に対応させていくのか、 delete_head,delete_tailの関数の部分に出る、head,tailは使えない的なエラーの対処の仕方がわからなく、詰まってます。 アドバイス、他にも直す場所等ありましたらお願いします。 #include<stdio.h> #include<stdlib.h> #define LINE 1000 typedef struct num{ char line[LINE]; struct num *next; struct num *prev; }Num; void normal(Num **,Num **,char *); void reverse(Num *,Num *,char *); void delete_head(void); void delete_tail(void); void delete(Num *target); int main(int argc,char *argv[]) { FILE *fp; char line[LINE]; int i=0,j; Num *head,*tail,*p; head=NULL; tail=NULL; fp=fopen("test.txt","r"); if(fp==NULL){ fprintf(stdout,"File not found.\n"); exit(1); } while(fgets(line,LINE,fp)!=NULL) { normal(&head,&tail,line); } p=head; while(p!=tail){ printf("%s\n",p->line); p=p->next; } printf("%s\n",p->line); p=tail; while(p!=head){ printf("%s\n",p->line); p=p->prev; } printf("%s\n",p->line); printf("input number\n"); scanf("%d",&j); p=head; for(i=0;i<=j;i++);{ p=p->next; } if(p==head){ delete_head(void); } else if(p==tail){ delete_tail(void); } else{ delete(p->prev); } p=head; while(p!=tail){ printf("%s\n",p->line); p=p->next; } printf("%s\n",p->line); fclose(fp); return 0; } void normal(Num **s,Num **e,char *g){ Num *tmp; int i; if(*s==NULL){ *s=(Num *)malloc(sizeof(Num)*1); (*e)=(*s); for(i=0;((g[i]!='\0')&&(g[i]!='\n'));i++){ (*s)->line[i]=g[i];} (*s)->next=*e; (*s)->prev=*s; } else{ tmp=(Num *)malloc(sizeof(Num)*1); for(i=0;((g[i]!='\0')&&(g[i]!='\n'));i++){ tmp->line[i]=g[i];} tmp->prev=(*e); (*e)->next=tmp; tmp->next=(*e); (*e)=tmp; } return ; } void reverse(Num *kan1,Num *kan2,char *g){ Num *tmp; int i; tmp=(Num *)malloc(sizeof(Num)*1); for(i=0;((g[i]!='\0')&&(g[i]!='\n'));i++){ tmp->line[i]=g[i];} tmp->prev=kan1; kan1->next=tmp; tmp->next=kan2; kan2->prev=tmp; return ; } void delete_head(void){ Num *second=head->next; if(secound==NULL){ free(head); head=NULL; tail=NULL; } else{ second->prev=NULL; free(head); head=second; } } void delete_tail(void){ Num *second_last=head->prev; if(second_last==NULL){ free(tail); head=NULL; tail=NULL; } else{ second_last->next=NULL; free(tail); tail=second_last; } } void delete(Num *target){ Num *after_target; Num *before_target; after_target=target->next; before_target=target->prev; after_target->prev=target->prev; before_target->next=target->next; free(target); }
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
★アドバイス >通常の表示の部分はできましたが、後は指定した行の削除の部分ができません。 ↑ 行で管理しているなら双方向リストを先頭から順番にたどって行き、指定の行番号(削除行)に 到達したとき、その行データを『双方向リストで削除』すれば良い。 先頭から順番にたどるので行番号をカウントして指定行と判定して処理します。 >あらかじめHPで調べてあるので、削除するときは、その削除したい行をまたいで、 >前後のリストを繋げるといった概念的なのは大体理解できました。 ↑ 大体理解できた。それならもう出来るのでは? ソースを見ると (1)delete_head() (2)delete_tail() (3)delete() の3つがありますね。どうせ分けるのならば (1)delete():双方向リストの削除メイン関数(下の,delete_head,delete_middle,delete_tailを呼ぶ) (2)delete_head():双方向リストの先頭データを削除 (3)delete_middle():双方向リストの中間データを削除 (4)delete_tail():双方向リストの末尾データを削除 の4つにしてみたらどうですか。 分かりやすく。 もちろん1つにまとめることも出来ますけど。 ・下にサンプルを少しだけ載せます。 サンプル: // 指定の行番号で削除するタイプ extern int delete( Num **pHead, Num **pTail, int no ) { Num *pFind; // 検索用 int i; // 削除行の検索 for ( i = 1, pFind = *pHead ; pFind != NULL ; pFind = pFind->next, i++ ){ if ( i == no ){ if ( pFind == *pHead ){ return delete_head( pHead, pTail, pFind ); // 先頭データの削除 } if ( pFind == *pTail ){ return delete_tail( pHead, pTail, pFind ); // 末尾データの削除 } return delete_middle( pHead, pTail, pFind ); // 中間データの削除 } } return 0; // 削除エラー(削除行が見つからない) } // 先頭データの削除 static int delete_head( Num **pHead, Num **pTail, Num *pItem ) { : (記述) : return 0; // 削除エラー } // 末尾データの削除 static int delete_tail( Num **pHead, Num **pTail, Num *pItem ) { : (記述) : return 0; // 削除エラー } // 中間データの削除 static int delete_middle( Num **pHead, Num **pTail, Num *pItem ) { : (記述) : return 0; // 削除エラー } 呼び出し例: int main( void ) { Num *Head = NULL; Num *Tail = NULL; int no; : (省略) : do { printf( "削除番号の入力:" ); scanf( "%d", &no ); } while ( no < 0 ); if ( delete(&pHead,&pTail,no) ){ printf( "削除OK!\n" ); } else{ printf( "削除NG!\n" ); } : (省略) : return 0; } その他: ・上記のサンプルの delete() 関数が行番号の指定で削除する削除メイン関数です。 その中で呼ばれている3つの delete_head()、delete_tail()、delete_middle() 関数は ご自分で作成して下さい。この3つの関数では正常に削除できたら 1 を返しエラーなら 0 を返す仕様です。 ・あと delete() 関数で指定する行番号は 1 が 1行目を意味します。 マイナスの数を渡しても削除エラーになるだけです。 ・それから4つの関数は第一引数、第二引数共にポインタのポインタですので注意して下さい。 delete() 関数で『pFind = *pHead』と『*pHead』となっている部分に注目。 削除するデータが先頭や末尾の場合は pHead、pTail に新しいデータのポインタを セットしますが次のようにします。 (1)新しい先頭データのポインタをセット⇒*pHead = pItem; (2)新しい末尾データのポインタをセット⇒*pTail = pItem; ※pHead、pTail 共に『*』が付く点に注意して下さい。⇒理由:ポインタのポインタなのでつくの。 ・以上。3つのサブ関数を頑張って作成して下さい。
その他の回答 (3)
- yaemon_2006
- ベストアンサー率22% (50/220)
#include <stdio.h> #include <stdlib.h> #include <string.h> enum {NOR, REV}; typedef struct line{ char *str; struct line *next; struct line *prev; } LINE; int readfile(LINE *hook, char *fname) { FILE *fin = fopen(fname, "r"); char stemp[256]; hook->next = hook->prev = hook; if(!fin) return 0; while(fgets(stemp, 256, fin)){ if(!(hook->prev->next = malloc(sizeof(LINE)))) return 0; if(!(hook->prev->next->str = malloc(strlen(stemp) + 1))) return 0; strcpy(hook->prev->next->str, stemp); hook->prev->next->prev = hook->prev; hook->prev->next->next = hook; hook->prev = hook->prev->next; } fclose(fin); return 1; } void show(LINE *hook, int rev) { LINE *ltemp = rev ? hook->prev : hook->next; if(!hook) return; while(ltemp != hook){ printf(ltemp->str); ltemp = rev ? ltemp->prev : ltemp->next; } return; } int deleteline(LINE *hook, int n) { LINE *ltemp = hook->next; int i; if(n <= 0) return 0; for(i = 1; ltemp != hook; ltemp = ltemp->next){ if(i ++ == n){ ltemp->prev->next = ltemp->next; ltemp->next->prev = ltemp->prev; free(ltemp->str); free(ltemp); return 1; } } return 0; } void freelist(LINE *hook) { if(!hook) return; while(hook->next != hook){ hook->next = hook->next->next; free(hook->next->prev->str); free(hook->next->prev); hook->next->prev = hook; } return; } int main(int argc, char **argv) { LINE hook; int n; if(argc != 2) return 1; if(!readfile(&hook, argv[1])) return 1; do{ show(&hook, NOR); printf("Line Number? : "); scanf(" %d", &n); printf(" -- Delete Line %d --\n", n); deleteline(&hook, n); }while(n > 0); show(&hook, NOR); puts(" -- Reverse --"); show(&hook, REV); freelist(&hook); return 0; }
- redfox63
- ベストアンサー率71% (1325/1856)
headやtailは main関数のローカル変数ですから 外部の delete_headやdatele_tailからは参照できなくて当たり前です 引数でも渡していませんからね 双方向リストなら 先頭ノードのprevメンバーはNULL 最終ノードのNextメンバーもNULL としておけば Delete関数は1種類で可能ですよ void dalete(Num* pnode, Num* head, Num* tail ) { NUM* NextNode = pnode->next; NUM* PrevNode = pnode->prev; if ( !PrevNode ) { // 前のノードがある PrevNode->next = NextNode; } else { // 前のノードが無い … つまり先頭 head = NextNode; } if ( !NextNode ) { // 次のノードがある NextNode->prev = PrevNode; } else { // 次のノードが無い … つまり最後 tail = PrevNode; } // 接続変更完了につきノードを開放 free( pnode ); } nextやprevが循環参照なら ifの判断を変更してください
- koko_u_
- ベストアンサー率18% (459/2509)
根本的なところがわからんのですが、削除する行の指定は行番号でするんですよね? なぜファイル内容を双方向リストに格納しているのですか?配列で十分なのでは?