• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:[C言語]単連結リストにおける着目ノードの操作)

[C言語]単連結リストにおける着目ノードの操作

このQ&Aのポイント
  • 単連結リストにおける着目ノードの操作について
  • 着目ノードを1個次にずらす操作と、1個前にずらす操作を追加したい
  • 単連結リストでは後者の実現は少しややこしい

質問者が選んだベストアンサー

  • ベストアンサー
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.7

「明白に質問者を置き去りにしている」としか思えないので「先頭ノードから後続ノードへのポインタを辿っていく」バージョンも書いてみる... けど, 本質的に「next が『着目しているノード』であるようなノード」をひたすら探すだけなので構造的には SearchNode と変わりません: void PrevCrnt(List *list) { if (list->crnt == NULL) { puts("着目要素はありません。"); } else if (list->crnt != list->head) { Node *p = list->head; while (p != NULL) { if (p->next != list->crnt) { list->crnt = p; return; } p = p->next; } } } 本当に正しいことまでは保証できないので確認はお願いしますね. それにしても「crnt」はどうしても「クルント」って読んじゃうなぁ. こんな無理して縮めずとも「current」でいいのに. NO とか NAME とかも, シフト使えば... いやいや, こんなところであら捜ししてもしょうがないんだ.... で蛇足タイム (笑): ウケ狙いで #define defStruct(tag) typedef struct tag tag; struct tag とかやると #3 の最初の 2行が typedef struct Node Node; struct Node { から defStruct(Node) { に変わるのでちょっと見やすい... か? 祝 gets消滅 (謎)

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (7)

回答No.8

>defStruct(Node) { おお、なんとw これはC++に近くなってますねw では私は確認済みのやつを提供するために 自分のコードへの拡張、という形で示しますが こんなの思いつきましたよ 「入れ替えなしで、numを逆順に表示する!」 void PrintAll_Reversed( CPNODE p ){ if ( !p ) return; PrintAll_Reversed( p->next ); printf( "%d\r\n", p->num ); } スタックを使ってprevポインタを疑似的に作り出す、って言う考え方です。 もちろん、ここまでくると こいつは魔術の一種に近いんで、極力不必要に真似しないでくださいw (もちろん、再帰が必要な構造だったら再帰は使うべきでしょうが、線形リストなら普通ループでかかないとスタック無駄に使っちまう危険があります。) puts( "\r\n逆順表示\r\n" ); PrintAll_Reversed( head ); こんなコードで確認できるかと。

全文を見る
すると、全ての回答が全文表示されます。
回答No.6

フムフム、そういうことですね OKです。 >struct __node >は危険です. それなんですね #3言われたとこで気づいたんですが 仮に「あの時点」までで、私が「完全に0から書いてたら」struct node_こうなった可能性が高いですが(後ろについてるのは基本だいじょぶですから) なんでうっかりこうなってたかって言うと、「リンク先のコピペ」が原因だったんですよねw 元 typedef struct __node { Data data; /* データ */ struct __node *next; /* 後続ノードへのポインタ */ } Node; で、下のサンプルではint型一個あれば十分と思ったので そこからさらに、後の拡張があった場合にアラインメント的に無駄が少なくなる可能性が高いであろう、となるように、配置逆にして(ポインタの方がサイズが大きくなる可能性高いと思うので) typedef struct __node { struct __node *next; int num; } Node; こうなったわけですが ※なお、アラインメントについては http://www5d.biglobe.ne.jp/~noocyte/Programming/Alignment.html 辺りを参照していただくとして 下のサンプルを仮に直すとしたら typedef struct Node Node, *PNODE; struct Node { PNODE next; int num; }; ぐらいでよさそうって感じですね。(もっとも、10文字以内程度の差ぐらいですが) なお、ポインタのポインタについてですが もうちょっと詳しく言うと 普通に全体に特定の処理かけるだけとかなら、単なるポインタの方が簡単だし、表記上も処理上も無駄がない可能性高いですが 「挿入や削除、追加などをはじめとする、付け替えの時」にポインタのポインタ使うと非常にすっきりとして、便利な可能性が高いです。

全文を見る
すると、全ての回答が全文表示されます。
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.5

そういえば #3 では本題について全く触れていなかったのでそっちについてちょっとだけ書いておこう. #1 でも書かれていますが, 単方向リストを作るときに「ポインタのポインタ」を使うと便利なことがしばしばあります (これは 2文木でも同様). なので, 探索なども含めてすべて「ポインタのポインタ」をベースに関数を作っていくとプログラムが書きやすくなる傾向があります. たとえば, 現状探索関数は Node *SearchNode(List *list, Data x, int equal(Data x, Data y)) { Node *ptr = list->head; /* 以下は略 */ } で始まっているんだけど, これを Node **pp = &list->head; としちゃう (もちろん返り値も Node **) とあとがちょっとだけ楽かもしれません. 以下は蛇足: まず, typedef では不完全型に別名をつけることもできます. なので, 構造体 Foo を定義しなくても typedef struct Foo Bar; は合法です. さらに, 構造体タグ (もちろん共用体および列挙型のタグを含む) と「ふつ~の識別子」とは異なる名前空間 (≠ C++ の namespace) に属し, その間では同じ識別子を使っても問題ありません (ついでにいうとラベルもまた別の名前空間になる). なので typedef struct Node Node; struct Node { Node *next; int num; }; typedef Node *PNode; は文法的に正しいです (C++ でも正しい) し, typedef struct __node { struct __node* next; int num; } Node; という typedef ができる以上 typedef struct Node Node; typedef struct Node { Node *next; int num; } *PNode; も OK です. まあ, 現実的にはこうするくらいなら typedef struct Node Node, *PNode; struct Node { Node *next; int num; }; (あるいは PNode next; とする) ですけどね. あ, 今気づいた. #3 を書く時点では見えなかったんだけど, 「_+大文字」や「__」で始まる識別子はすべての用途において処理系に予約されているので struct __node は危険です.

全文を見る
すると、全ての回答が全文表示されます。
回答No.4

>typedef struct Node Node; >struct Node { >Node *next; >int num; >}; >typedef Node *PNode; なるほど、これもOKなんですねw Cの深淵に対する理解度はまだまだあまっちょろいですね~w(←自分に対して) ついでに、Cではこれっておkでしたっけ? (うちんとこでは動くっぽいですが) typedef struct Node Node; typedef struct Node { Node *next; int num; } *PNode; んでもまぁ、C++なら、「メンバ関数抜き」にしても、さらに1行減って、いきなり typedef struct Node { Node *next; int num; } *PNode; これぐらいのことが出来ちゃうし、ローカル変数の宣言位置はC99みたいに「ブロック先頭じゃなくてもいい」→ for(int i=0;i<NUM_;++i){~} とかC++03でも普通に可能 ですし、メンバ関数持てますし、テンプレートあって継承あってアクセス修飾子まであって、C++11も固まった、ということで Cを使うなら、どうせなら「C++も読み書きできるようになる」ってことはやはりとてもお勧めですね。

全文を見る
すると、全ての回答が全文表示されます。
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.3

どうせ typedef するなら, 構造体タグにも同じものを使えばいいんじゃないでしょうか>#2. typedef struct Node Node; struct Node { Node *next; int num; }; typedef Node *PNode; でも動きますし.

全文を見る
すると、全ての回答が全文表示されます。
回答No.2

そうですか。 プログラムってのは不必要な機能は書かない方がいいです(コンパイル時間も犠牲になる) が、知的好奇心を満たすために やってみるというのも面白いかもしれません。 ただ、Cの長いソース読み切るのは結構辛いので もうちょっと機能が少ない(最後尾をさすポインタはないなど)バージョンを「別途0から書いて」みました。 (最近C++漬なので、Cだと出来ない表記が入っちゃってるかもしれませんが てかやっぱCとC++ではC++の方が読みやすいと思うので、それをご希望される場合はそっちで書きなおせます。) 単方向の線形リストでは 実用的に前後のポインタの情報がいると便利と思われるのは 「挿入」 「順番入れ替え」 などなどぐらいなものですから 前者をInsert(先頭からNodeの「num」が昇順になるように挿入) 後者をPlacement_Reversed(逆順にポインタを総付け替え) として、ポインタのポインタを有効活用しつつ実現してみました。 (教えてgooだとインデントがなくなるので余計読み辛いかもしれませんがw コピペして正常に動いたら、その辺修正しつつ読み解いて見てください。どうにも分からない箇所あったら聞いてください) #include <stdio.h> #include <stdlib.h> typedef struct __node { struct __node* next; int num; } Node; typedef const Node* CPNODE; typedef Node* PNODE; int GetCount( CPNODE p ){ int i = 0; for ( ; p ; p = p->next ) ++i; return i; } void PrintAll( CPNODE p ){ for ( ; p ; p = p->next ) printf( "%d\r\n", p->num ); } void Placement_Reversed( PNODE* head_ ){ //逆順に並び変える PNODE p = *head_; const int num_of_nodes = GetCount( p ); PNODE* pp = (PNODE*)malloc( sizeof(p) * num_of_nodes ); int i = 0; for ( ; p ; p = p->next ) pp[i++] = p; //先に順番に入れておく. //i == num_of_nodes; になってる for ( ;i; head_ = &(*head_)->next ) *head_ = pp[--i]; //逆順につけかえ pp[0]->next = NULL; free( pp ); } void Insert( PNODE const newnode, PNODE* pp ){ PNODE p; for ( ; p = *pp ; pp = &p->next ){ if ( newnode->num <= p->num ) break; } newnode->next = *pp; *pp = newnode; } int main(void){ const int numbers[] = { 4, 3, 8, 6, 0, -1, 0 }; enum { NUM_ = sizeof(numbers) / sizeof( numbers[0] ) }; PNODE head = NULL, p; Node nodes[NUM_]; int i; for ( i = 0; i < NUM_ ; ++i ){ p = nodes + i; p->next = NULL; p->num = numbers[i]; Insert( p, &head ); } PrintAll(head); Placement_Reversed( &head ); puts( "\r\n順番入れ替え後\r\n" ); PrintAll(head); getchar(); //一瞬で終わらないようにするためだけ return 0; }

全文を見る
すると、全ての回答が全文表示されます。
回答No.1

(こうしてみるとCとC++(どっちもものすごく練られているコードと仮定して)では読みやすさに雲泥の差があるなぁ。柴田さん達のコードでこれですからね…まぁもうちょっと読みやすくは出来るだろうけども。あと構造体はポインタ渡しのが良いと思いますが、まぁそこはおいといて) 単方向連結リスト って言うのは、通常一方向の操作だけで簡単に済むような場合に使うものです。 着目ノードの前後移動を自由に行いたいのならば、通常は双方向連結リスト(nextとprevをもつ)を使います。 ただし よほどの場合で 単方向連結リストでごく稀にだけ そうしたい場合は >おそらく先頭ノードから後続ノードへのポインタを辿って行って、着目ノードを探索すれば良いのでしょうが……。 そうすることでも出来ますし(スマートでない可能性は高い) もう一個だけポインタあればいいのならもう一個用意すればできますし ポインタのポインタを使えばちょー楽勝なこともありますし よっぽど連続でごしゃごしゃいじる場合は 作業前に先にポインタ配列作っといてそっちに順番に移し替えたりしといて…とかでもできます。 もちろんそれぞれに一長一短ありますが 「単方向連結リストを使うのが適切な構造では」ほとんどの場合 ポインタ2つとか、ポインタポインタで十分解決できると思います。 具体的にはどういう用途がお望みですか?

Falry_taie
質問者

お礼

回答ありがとうございます。 用途は特に想定していません。こういった場合双連結リストを使うべきなのは承知しておりますが、ひねくれた性分でして…。 授業で習ったプログラムをどうにかして弄くり回そうと、いろいろやってみたくなるんです。まぁ、テスト勉強も兼ねていますが。 ご提案いただいた方法でも試してみますね。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • C言語 線形探索

    #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXBUFFERSIZE 256 struct LinkedListNode{ int data; struct LinkedListNode *next; }; struct LinkedList{ int node_num; struct LinkedListNode *head; }; LinkedList *LinkedListMake(char *filename) { FILE *fp; LinkedList *list; char buffer[MAXBUFFERSIZE]; /* ファイル有無のチェック */ if ((fp = fopen(filename, "r")) == NULL) { fprintf(stderr, "No Such File : %s\n", filename); exit (1); } list = LinkedListAlloc(); if (list == NULL) { /* 領域確保失敗 */ exit (0); /* 終了 */ } while (fgets(buffer, MAXBUFFERSIZE, fp)) { /* ファイル終端に到達するまでループ */ buffer[strlen(buffer) - 1] = '\0'; /* 改行文字を削除 */ LinkedListDataAdd(list, atoi(buffer)); } fclose(fp); return (list); } LinkedList *LinkedListAlloc(void) { LinkedList *list; list = (LinkedList *)malloc(sizeof(LinkedList)); if (list == NULL) { /* 領域確保失敗 */ return (NULL); } list->node_num = 0; list->head = NULL; return (list); } LinkedListNode *LinkedListDataAdd(LinkedList *list, int x) { LinkedListNode *ptr; /* 注目するノードへのポインタ */ LinkedListNode *prev; LinkedListNode *new_node; ptr = list->head; prev = NULL; while (ptr) { /* 終端ノードに到達するまでループ */ if (ptr->data < x) { prev = ptr; /* 直前ノードの更新 */ ptr = ptr->next; /* 注目ノードの更新 */ } else if (ptr->data == x) { /* x は登録済み */ return (NULL); } else { /* x を注目ノードの直前に追加 */ new_node = LinkedListNodeAlloc(); if (new_node == NULL) { /* 領域確保失敗 */ exit (0); /* 終了 */ } new_node->data = x; new_node->next = ptr; /* ポインタの付け替え(注目ノードの直前) */ if (prev != NULL) { /* 連結リストの先頭以降に追加 */ prev->next = new_node; /* ポインタの付け替え */ } else { /* 連結リストの先頭に追加 */ list->head = new_node; } list->node_num++; /* ノード総数の更新 */ return (new_node); } } /* 終端ノードに到達 */ /* x を終端に追加 */ new_node = LinkedListNodeAlloc(); if (new_node == NULL) { /* 領域確保失敗 */ exit (0); /* 終了 */ } new_node->data = x; new_node->next = NULL; /* new_node は新たな終端ノード */ if (prev != NULL) { /* list は少なくともひとつのノードを有している */ prev->next = new_node; /* 更新前の終端ノードの直後が new_node となる */ } else { /* list は空(ノードがひとつも含まれない) */ list->head = new_node; } list->node_num++; /* ノード総数の更新 */ return (new_node); } LinkedList *LinkedListSearch(LinkedList *list, int x)←ここがわかりません★ { for(i = 0; i < node_num)      ??? int main(int argc, char *argv[]) { int a, i, x; printf("xの値を入力"); scanf("%d", &x); LinkedListMake(argv[1]); LinkedListSearch(list, x); 連結リストに格納されたint型データから目的の値を線形探索するプログラムをつくってます。 連結リスト作成関数まではできたので、あと連結リストにおいて目的の値を線形探索する関数LinkedListSearchをつくればだいたい完成だと思うのですが、関数LinkedListSearchの作り方がわかりません。 引数で連結リストのポインタと目的値をとって、目的値が存在すればそのノードのポインタ、存在しない場合はNULLを返すようにするつもりです。 わかる方、是非とも教えてください! お願いいたします。

  • C言語(BCB)で双方向リストを操作する方法

    Borland C++ Builder5 でプログラミングしています。 親Formで、ファイルを読み、双方向線形リストを作っています。 子Form(ダイアログ)で、この線形リストにノードを追加したいのですが、うまくコンパイル出来ません。 どなた様か、ご指導お願いします。 まず、構造体として次のように定義しています。(Form1のヘッダーファイル) typedef struct _UkeM{ char id[7]; // 途中略 struct _UkeM *beforP; struct _UkeM *nextP; } UkeM; UkeM *UkeTop, *UkeNow, *UkeEnd; 当然これらの定義は、子Formでも操作したいので、public: として定義されています。 親Formで領域を確保するには、 UkeNow = new UkeM; と実行し、最尾ノードのnextPなどにアドレスを格納しています。 この親Formでの処理はコンパイルも通り正しく実行されています。 さて、子Formで、このリストにノードを作ってつなげたい、と思っています。 子Formでは、単純に Form1->UkeNow = new Form1->_UkeM; とやってみたのですが、「型名が必要」というエラーで通りません。 どうも、newの右側が良くないようです。 子Formで、このリストを参照するだけなら全く問題なく操作できています。 (例えば、Label1->Caption = Form1->UkeNow->id;) newだけがうまく出来ません。 子Formでのnewのやり方についてご指導をお願いします。

  • c言語 片方向連結リスト

    c言語の片方向連結リストのプログラムについて質問があります. 下記のプログラムの関数int get_index(ListPtr l, int value)に以下のようなコードを書く.リストl において値value を持つセルの位置を返す.返り値は,最初のセルが値value を持っていれば0,次のセルが値value を持っていれば1,...,値value を持っているセルが存在しなければ–1とする. また,関数void add(ListPtr l, int index, int value)に以下のようなコードを書く.リストl の位置index に値value を持つセルを挿入.挿入前のリストに対して:index が0 のときは先頭に挿入,index が1 のときは(0から数えて)1番目のセルの前に挿入,index が2 のときは(0から数えて)2番目のセルの前に挿入,...,index がリストのサイズと等しいときはリストの末尾に挿入,それ以外の場合は何もしなくてよい. これらのコードはどのように書けばよいのでしょうか? #include <stdio.h> #include <stdlib.h> #include <assert.h> /* 連結リスト中のノードの構造体 */ struct node { int val; /* 値 */ struct node *next; /* 次ノード */ }; /* セルとそのポインタの型 */ typedef struct node Node; typedef Node *NodePtr; /* セルを一つ生成 */ NodePtr create_node(int v) { NodePtr n = NULL; n = malloc(sizeof(Node)); n->val = v; n->next = NULL; return n; } /* セルを表示 */ void print_node(NodePtr n) { if (n != NULL) { printf("<%d>", n->val); } else { printf("(null)"); } } /* 連結リストの構造体 */ struct list { /* 先頭セルへのポインタ */ NodePtr head; }; /* 連結リストとそのポインタの型 */ typedef struct list List; typedef List *ListPtr; /* 空の連結リストを生成 */ ListPtr create_list(void) { ListPtr l = NULL; l = malloc(sizeof(List)); l->head = NULL; return l; } /* 連結リスト l が空かどうか判定 */ int is_empty(ListPtr l) { return (l->head == NULL); } /* リスト l の内容を表示 */ void print_list(ListPtr l) { NodePtr n = NULL; if (is_empty(l)) { printf("(empty)\n"); return; } n = l->head; while (n != NULL) { print_node(n); n = n->next; } printf("\n"); } /* リスト l の先頭にセルを挿入 */ void add_first(ListPtr l, int val) { NodePtr n = NULL; n = create_node(val); n->next = l->head; l->head = n; } /* リスト l の先頭セルを削除 */ int delete_first(ListPtr l) { NodePtr n = NULL; int v; /* リストが空なら -1 を返す(負の値はリストに含まれないと仮定)*/ if (is_empty(l)) return -1; v = l->head->val; n = l->head; l->head = l->head->next; free(n); n = NULL; return v; } /* 連結リスト l のサイズを取得 */ int get_list_size(ListPtr l) { NodePtr n = NULL; int size; size = 0; n = l->head; while (n != NULL) { size++; n = n->next; } return size; } /* * 連結リスト l における index 番目のセルの値を取得 * (そのようなセルが存在しなければ -1 を返す) */ int get_value(ListPtr l, int index) { NodePtr n = NULL; if (index < 0) return -1; n = l->head; while (index > 0 && n != NULL) { n = n->next; index--; } return (n == NULL) ? -1 : n->val; } /* リスト l 中の全セルを削除(ループ版)*/ void delete_all(ListPtr l) { NodePtr n = NULL, m = NULL; n = l->head; while (n != NULL) { m = n; n = n->next; free(m); } l->head = NULL; } /* セル n 以降を全て削除(内部処理用の再帰関数)*/ void delete_rest(NodePtr n) { if (n->next != NULL) delete_rest(n->next); free(n); } /* リスト l 中の全セルを削除(再帰版)*/ void delete_all_recursively(ListPtr l) { if (l->head == NULL) return; delete_rest(l->head); l->head = NULL; } /* リスト l 全体を削除 */ #define delete_list(l) (delete_list0(l),l=NULL) void delete_list0(ListPtr l) { delete_all(l); free(l); } /* リスト l において値 val を持つセルの位置を返す */ int get_index(ListPtr l, int value) { return -1; } /* リスト l の位置 index に値 val を持つセルを挿入 */ void add(ListPtr l, int index, int value) { } /* 連結リストの使用例 */ int main(void) { FILE *fp = NULL; char buf[10]; int age; ListPtr l = NULL; l = create_list(); add_first(l, 28); add_first(l, 40); add_first(l, 33); add_first(l, 15); print_list(l); delete_first(l); print_list(l); delete_first(l); print_list(l); delete_first(l); print_list(l); delete_first(l); print_list(l); return 0; }

  • C++ 連結リストの要素の削除について

    リストで任意の場所の要素を削除するプログラムを考えているのですが、どうしてもその部分のプログラムにmain関数通りの動きをさせる方法が分かりませんでした。いろいろ試してみたのですが、どこかしらでおかしくなってしまうのです。 以下に私がつくった途中までのプログラムを示すので、removeAt(int index)の中身の例を教えて頂きたいのです。よろしくお願い致します。 #include <iostream> using namespace std; // クラスの宣言 class Cell { friend class List; // Listクラスから,このクラスに自由にアクセスできるようにする private: int data; // データ Cell *next; // 次のセルのアドレスを指すポインタ public: Cell(int _data, Cell *n = NULL){ data = _data; next = n; } }; class List { private: Cell *head; // 連結リストの先頭要素のアドレスを指すポインタ public: List(){ head = NULL; }; ~List(){ while (head != NULL)removeFirst(); } void addFirst(int data){ head = new Cell(data, head); } void removeFirst(){ if (head == NULL)return; Cell *removed = head; head = head->next; delete removed; } void removeAt(int index); void print(){ for (Cell *p = head; p != NULL; p = p->next){ cout << p->data << ' '; } cout << endl; } }; // 連結リストの index 番目の要素を削除する関数 void List::removeAt(int index) { //この部分です } int main() { List l1; for (int i = 0; i < 10; i++) { l1.addFirst(i); } l1.print(); l1.removeAt(0); // リストの先頭要素で動作確認 l1.print(); l1.removeAt(2); l1.print(); l1.removeAt(4); l1.print(); l1.removeAt(17); // リストの要素数より大きな引数で動作確認 l1.print(); l1.removeAt(0); // リストの先頭要素で動作確認 l1.print(); l1.removeAt(6); // リストの要素数より大きな引数で動作確認 l1.print(); l1.removeAt(5); l1.print(); return 0; } 要求される実行結果は以下の通りです リストの内容: 9 8 7 6 5 4 3 2 1 0 リストの内容: 8 7 6 5 4 3 2 1 0 リストの内容: 8 7 5 4 3 2 1 0 リストの内容: 8 7 5 4 2 1 0 リストの内容: 8 7 5 4 2 1 0 リストの内容: 7 5 4 2 1 0 リストの内容: 7 5 4 2 1 0 リストの内容: 7 5 4 2 1

  • 双方向リストの関数

    双方向リストにデータファイルから読み込んだ氏名と成績のデータを追加し,リストの末尾から順にデータを表示するプログラムを作成したのですが、insertLast() 関数を用いて末尾ノードの後ろに新しいノードを連結するという部分をどのようにして良いのかわからず困っています。 どのように記述して良いのかわからなかった部分を/*** ***/のコメントで示してあります。 どなたかアドバイスやヒント、その他の指摘などご教授してくださる方いましたらよろしくお願いします。 #include <stdio.h> #include <stdlib.h> #include <string.h> #define NAME_LENGTH 20 /* 名前を格納する文字列の長さ */ /* 双方向リストのノードとなる構造体の定義 */ typedef struct sList{  struct sList *prev; /* 前のノードのアドレス */  char name[NAME_LENGTH]; /* 名前 */  char grade; /* 成績 */  struct sList *next; /* 次のノードのアドレス */ } sNode; /* この構造体を sNode型 と定義する */ /* 双方向リストの先頭と末尾を格納するための構造体 */ typedef struct {  sNode *firstNode; /* リストの先頭ノードのアドレス */  sNode *lastNode; /* リストの末尾ノードのアドレス */ } manageList; /*  双方向リストの末尾にノードを追加する関数 insertLast()  引数   manageList *list リストの先頭・末尾ノードを管理する構造体のアドレス   sNode *node 末尾に追加したいノードのアドレス  返値   なし */ void insertLast(manageList *list, sNode *node ) {  /*** リストの末尾にノードを追加する ***/  return; } /*  双方向リストの新しいノードを作成する関数 makeNewNode()  引数   char *aName 名前(文字列)の先頭アドレス   char aGrade 成績  返値   sNode * 新しく作成したノードの先頭アドレス */ sNode *makeNewNode(char *aName, char aGrade ) {  sNode *pNewData;  /* sNode 型のメモリ領域を確保 */  pNewData = (sNode *) malloc( sizeof(sNode) );  /* 名前と成績のデータを設定する */  strcpy( pNewData->name, aName );  pNewData->grade = aGrade;  pNewData->prev = NULL;  pNewData->next = NULL;  return( pNewData ); } /*  main()  引数   なし  返値   int 正常終了の時 0     異常終了の時 -1 (ファイルの読み込み失敗など) */ int main( void ) {  manageList list; /* リストの先頭・末尾ノードのアドレスを持つ構造体 */  sNode *pNew; /* 新しく作成したノードのアドレスを持つ変数 */  sNode *pNow; /* 現在見ているノードのアドレスを持つ変数 */  FILE *fp; /* データファイルのファイルポインタ */  char name[ NAME_LENGTH ]; /* ファイルから読み込んだ名前を一時的に保持する変数 */  char grade; /* ファイルから読み込んだ成績を一時的に保持する変数 */  /* 初期状態では先頭・末尾ノードともNULL */  list.firstNode = NULL;  list.lastNode = NULL;  /* データファイル exer6.txt を読み込み用に開く.    ファイルが開けなかった場合,エラーメッセージを表示し異常終了する.*/  fp = fopen("exer6.txt","r");  if(NULL == firstNode){   printf( "ファイルが開けませんでした. \n" );   return( -1 );  }  /* データをファイルの最後(EOF)まで読み込み,双方向リストにデータを追加する */  while( EOF != fscanf( fp, "%s %c", name, &grade ) ) {   /* 新しいノードを作成 */   pNew = makeNewNode( name, grade );   /*** insertLast() 関数を用いて末尾ノードの後ろに新しいノードを連結する ***/   pNow->next = pNew ;   pNew->prev = pNow ;   pNow = pNew ;  /* ファイルを閉じる */  fclose( fp );  /* 現在見ているノードを末尾ノードにセットする */  pNow = lastNode  /* 末尾のノードから前のノードへたどりながらデータを出力する */  while( NULL != pNow ) {   /* 出力 */   printf( "%s %c\n", pNow->name, pNow->grade );   /* 現在見ているノードを一つ前のノードにする */   pNow = pNow->prev;  }  return( 0 ); }

  • 連結リストをソート

    学校の課題なんですが正直手も足も出ません。 どういった流れで作成すればいいんでしょうか。 1.連結リストにデータ(文字列)をソートされた順序に追加するようなプログラムを作成する. 2.連結リストのデータを順にプリントするプログラムを作成する。 3.セルをキーとそれに対応する値を含めるように拡張し、与えられたキーを持つセルを探索して  それに対する値を返すプログラムを作成する。 課題を解く際には以下のプログラムを参考にする #include <stdio.h> struct element{ char data; struct element *next; }; struct elements *new() { return((struct element *)malloc(sizeof(struct element))); } struct element *create() { struct element *p; p=new(); p->next=NULL; return(p); } void insert(struct element *l,int k,char item) { struct element *p; if(k>1) insert(l->next,k-1,item); else{ p=new(); p->data=item; p->next=p; l->next=p; } } void delete(struct element *l,int k) { if(k>1) delete(l->next,k-1); else l->next=l->next->next; } char access(struct element *l,int k) { if(k>1) return(access(l->next,k-1)); else return(l->next->data); }

  • C++ リストの末尾要素の削除

    連結リストの末尾の要素を削除するプログラムを教えてください。 自分で何度も書き直してみたのですが、どうしてもうまくうごきません。 自分で書いた現状が以下のものですが、止まってしまいます。 void List::removeLast(){ if (head == NULL)return; Cell *p = head; Cell *prev = NULL; while (p!= NULL){ prev = p; p = p->next; } delete prev; } 以下のプログラムが動かすにはremoveLast()をどう変えたらよいのでしょうか? よろしくお願いします。 #include <iostream> using namespace std; // クラスの宣言 class Cell { friend class List; // Listクラスから,このクラスに自由にアクセスできるようにする private: int data; // データ Cell *next; // 次のセルのアドレスを指すポインタ public: Cell(int _data, Cell *n = NULL){ data = _data; next = n; } }; class List { private: Cell *head; // 連結リストの先頭要素のアドレスを指すポインタ public: List(){ head = NULL; }; ~List(){ while (head != NULL)removeFirst(); } void addFirst(int data){ head = new Cell(data, head); } void removeFirst(){ if (head == NULL)return; Cell *removed = head; head = head->next; delete removed; } void removeLast(); void print(){ for (Cell *p = head; p != NULL; p = p->next){ cout << p->data << ' '; } cout << endl; } }; // 連結リストの末尾要素を削除する関数 void List::removeLast() { //この部分です } int main() { List l1; for (int i = 0; i < 10; i++) { l1.addFirst(i); } l1.print(); l1.removeLast(); l1.print(); l1.removeLast(); l1.print(); l1.removeLast(); l1.print(); return 0; } 要求される実行結果は以下の通りです リストの内容: 9 8 7 6 5 4 3 2 1 0 リストの内容: 9 8 7 6 5 4 3 2 1 リストの内容: 9 8 7 6 5 4 3 2 リストの内容: 9 8 7 6 5 4 3

  • C言語のプログラムを書いたのですが上手く動きません

    C言語の練習問題をプログラミングしたのですが、上手く動きません。 コンパイルはできるのですが、実行すると「Segmentation fault(core dumped)」となります。 問題は、コマンドライン引数としてファイル名を指定したテキストファイルから読み込んだデータを,双方向リストに格納し,順番に表示するというものです。 テキストファイルは 10T5001 C 10T5002 A 10T5003 B 10T5004 C 10T5005 D 10T5006 B 10T5007 A 10T5008 D このように、IDとランクをランクABCDをスペースで区切ったもので、これをランクAから順番に表示させます。 上手く動けば ID: 10T5002, grade: A, ID: 10T5007, grade: A ID: 10T5003, grade: B ID: 10T5006, grade: B ID: 10T5001, grade: C ID: 10T5004, grade: C ID: 10T5005, grade: D ID: 10T5008, grade: D と表示されるはずなんですが・・・ これが僕の書いたプログラムです(長いです。ごめんなさい) #include <stdio.h> #include <stdlib.h> #include <string.h> #define ID_LENGTH 8 /* IDの長さ+ナル文字 */ #define EFOPEN -1 #define ENOMEM -2 #define EINVAL -3 /* 双方向リストのノード */ typedef struct sList /* タグ */ { char id[ID_LENGTH]; /* ID */ char grade; /* ランク*/ struct sList *prev; /* 前のノードのアドレス */ struct sList *next; /* 次のノードのアドレス */ } sNode; /* 双方向リストのノードを作成する関数 makeNewNode() 作成したノードのprevとnextはNULLにする 引数 ・ノードに格納するID ・ノードに格納するランク(A/B/C/D) 戻値 作成したノードの先頭アドレス.メモリの確保に失敗した場合はNULL */ sNode *makeNewNode(char *id, char grade) { sNode *pNewNode; pNewNode = (sNode*)malloc(sizeof(sNode)); if(pNewNode != NULL) { strncpy(pNewNode->id, id, ID_LENGTH-1); pNewNode->grade = grade; pNewNode->prev = NULL; pNewNode->next = NULL; } return pNewNode; } /* 引数で渡された任意のノードの後ろに新しいノードを追加する関数 insertNext() 引数 ・後ろにノードを追加したいノードの先頭アドレス ・追加するノードの先頭アドレス 戻値 なし */ void insertNext(sNode *node, sNode *newNode) { if(node->next == NULL) //一番後ろに追加する場合 { node->next = newNode; newNode->prev = node; } else //まん中に挿入する場合 { newNode->prev = node; newNode->next = node->next; node->next->prev = newNode; node->next = newNode; } } /* main() 引数 int argc コマンドライン引数の数 char *argv[] 与えられたコマンドライン引数の文字列の先頭アドレスの配列 戻値 int 正常終了の時 0 以下の場合は異常終了し,括弧内の値を返す データファイルが開けなかった場合(EFOPEN) makeNewNode()でメモリが確保できなかった場合(ENOMEM) 引数の数が正しくない場合(EINVAL) */ int main(int argc, char *argv[]) { sNode *top; /* リストの先頭ノードのアドレスを保持する変数*/ sNode *new; /* 新しく作成したノードのアドレスを保持する変数 */ sNode *now; /* 現在見ているノードのアドレスを保持する変数 */ FILE *fp; /* データファイルのファイルポインタ */ char id[ID_LENGTH]; /* ファイルから読み込んだIDを一時的に保持する変数 */ char grade; /* ファイルから読み込んだランクを一時的に保持する変数 */ /* コマンドライン引数の数をチェックする 数に過不足があれば,使い方を表示し,異常終了する */ if(argc != 2){ printf("Usage: %s datafilename.\n", argv[0]); return EINVAL; } /* データファイルを読み込み用に開く ファイルが開けなかった場合,エラーメッセージを表示し異常終了する */ fp = fopen(argv[1], "r"); if(NULL == fp){ printf("No such file %s.\n", argv[1]); return EFOPEN; } /* リストの先頭に番兵を立てる */ new = makeNewNode("Banpei", 'A'-1); if (new == NULL){ printf("Error: cannot allocate memory\n"); return ENOMEM; } top = new; /* データファイルから1行ずつデータを読み込み,ランク順にリストに追加していく 既にリストの先頭には番兵ノードがある点に注意 */ while(EOF != fscanf(fp, "%s %c", id, &grade)){ new = makeNewNode(id, grade); if(new == NULL){ printf("Cannot allocate memory.\n"); return EFOPEN; } if(top->next == NULL){ //リストが空の場合 insertNext(top, new); } else{ now = top; while(new->grade > now->grade){ now = now->next; } insertNext(now, new); } } /* できあがったリストの内容を先頭から順に表示する ただし,番兵ノードは表示しない */ now = top; while(now != NULL){ printf("ID: %s, grade: %c\n",now->id, now->grade,); now = now->next; } /* リストのノードを全て(番兵ノードも含む)解放し,リストを空にする */ now = top; while(now->next != NULL){ free(now->prev); now = now->next; } free(now); fclose(fp); return (0); } まだまだ練習中なもので、かなり拙いものだと思いますが、とりあえずまずはプログラム動くようにしたいです。 よろしくお願いします。

  • javaがわかりません。。。

    単方向リストをjavaで書きなさい。 MyListクラス   メソッドを実装 MyListAppクラス メソッドを実行 MyListに実装するメソッド void insertTail(MyList node):リストの最後へ新規データを追加 void insertHead(MyList node):リストの先頭へ新しいデータを追加 void show():現在のリストを表示する void insert(MyList node):リストの途中へ新規データを挿入 ※データが小さい順に並んでいるとして、新規データも小さい順になるような場所へ挿入できるようにする。 void delete(MyList node):リストからデータを1つ削除 ※指定されたデータを見つけ、最初に見つかったデータを削除できるしょうにする。見つからないときは、何もしない。 void deleteHead():先頭のノードを削除する void deleteTail():最後のノードを削除する という問題を解いています。 class MyList{ int data;     //データ MyList next = null; //次のノードへのポインタ MyList(int data){ //新しいノードの作成 this.data = data; } void insertTail(MyList node){ //リストの最後へ追加 MyList tmp = this; while(tmp.next != null){ tmp = tmp.next; } tmp.next = node; } void insertHead(MyList node){ //リストの先頭へ追加 node.next = this.next; this.next = node; } void insert(int head, MyList node){ //リストの途中へ新規データを挿入 MyList tmp = this; while(tmp != null){ if(tmp.data == head){ break; } tmp = tmp.next; } node.next = tmp.next; tmp.next = node; } void delete(int here, MyList node){ //リストからデータを1つ削除 MyList tmp = this; while(tmp != null){ if(tmp.data == here){ break; } tmp = tmp.next; } node = tmp.next; tmp.next = node.next; node.next = null; } void deleteHead(){ //先頭のノードを削除 MyList tmp = next; this.next = tmp.next; tmp.next = null; } void deleteTail(){ //最後のノードを削除 MyList tmp = this; while(tmp.next != null){ tmp = tmp.next; } tmp = null; } void show(){ MyList tmp = next; while(true){ System.out.print(tmp.data); if(tmp.next == null) break; tmp = tmp.next; } System.out.println(""); } } class MyListApp{ public static void main(String[] args){ MyList list = new MyList(0); //初期ダミー System.out.println("最後に追加"); list.insertTail(new MyList(1)); //最後に追加 list.show(); list.insertTail(new MyList(5)); list.show(); System.out.println(); System.out.println("先頭に追加"); list.insertHead(new MyList(8)); //先頭に追加 list.show(); list.insertHead(new MyList(9)); list.show(); System.out.println(); System.out.println("途中からデータを追加"); list.insert(1, new MyList(2)); //途中からデータの追加 list.show(); list.insert(2, new MyList(3)); list.show(); System.out.println(); System.out.println("リストからデータを削除"); list.delete(8, new MyList(1)); //リストからデータを削除 list.show(); System.out.println(); System.out.println("先頭のノードを削除"); list.deleteHead(); //先頭のノードを削除 list.show(); list.deleteHead(); list.show(); System.out.println(); System.out.println("最後のノードを削除"); list.deleteTail(); //最後のノードを削除 list.show(); list.deleteTail(); list.show(); } } ここまではできたのですが、どうしても最後のノード削除ができません。 どうしたらいいでしょうか。

    • ベストアンサー
    • Java
  • c言語 ハッシュ表

    下のハッシュ表のプログラムについて質問です.関数enterを二回呼び出して,valueの値を変更して表示させても値が更新されません.値が更新せれるにはどうすればよいのでしょうか? #include <stdio.h> #include <stdlib.h> #include <string.h> #define HASH_SIZE 997 /* ハッシュ表の内部配列のサイズ */ #define HASH_RADIX 97 /* ハッシュ関数用の基数 */ /* ハッシュ表内の連結リストに含まれるノードの構造体 */ struct hash_node { /* ハッシュ表内の連結リストのノード */ char *key; /* キー */ int value; /* キーに対応する値 */ int id; /* キーに付与された通し番号 */ struct hash_node *next; /* 次のノードへのポインタ */ }; typedef struct hash_node HashNode; typedef HashNode *HashNodePtr; /* ハッシュ表の構造体 */ struct hashtable { HashNodePtr *heads; /* 内部配列 */ int serial_id; /* 通し番号管理用の変数 */ int size; /* 内部配列のサイズ */ }; typedef struct hashtable HashTable; typedef HashTable *HashTablePtr; /* 文字列 s のハッシュ値を計算する */ unsigned int hash(char *s) { unsigned int v; v = 0; while (*s != '\0') { v = v * HASH_RADIX + *s; s++; } return v; } /* ハッシュ表を一つ生成し,そのポインタを返す */ HashTablePtr create_hashtable() { HashTablePtr t = NULL; int i; t = malloc(sizeof(HashTable)); t->serial_id = 0; t->size = HASH_SIZE; t->heads = malloc(sizeof(HashNodePtr) * t->size); /* 各連結リストの先頭要素へのポインタは必ず NULL に初期化する */ for (i = 0; i < t->size; i++) { t->heads[i] = NULL; } return t; } /* 指定したポインタ変数に NULL を代入して終わるためのマクロ */ #define delete_hashtable(t) \ (delete_hashtable0(t),t=NULL) /* 実質的な削除作業を行う関数(free 後の NULL 代入は省略)*/ void delete_hashtable0(HashTablePtr t) { HashNodePtr n = NULL, m = NULL; int i; /* 各連結リストの領域を解放 */ for (i = 0; i < t->size; i++) { n = t->heads[i]; while (n != NULL) { m = n; n = n->next; free(m); } } /* 最後に連結リストの先頭ポインタの領域を解放 */ free(t->heads); free(t); } /* ハッシュ表 t に登録されているキーと値のペアの数を返す */ int get_cardinality(HashTablePtr t) { return t->serial_id; } /* ハッシュ表 t にてキー key に対応する値を調べる */ int lookup(HashTablePtr t, char *key) { HashNodePtr n = NULL; int index; /* ハッシュ表の内部配列の添え字を計算 */ index = hash(key) % t->size; /* index 番目の連結リストを先頭から順に走査 */ n = t->heads[index]; while (n != NULL) { /* 引数で指定された key とハッシュ表内のキーが一致したら 直ちに対応する値を返す */ if (strcmp(key, n->key) == 0) return n->value; /* 走査を次に進める */ n = n->next; } /* ここではキーに対応する値は非負であると仮定し, 見つからなかったら -1 を返す */ return -1; } /* キー key と値 value のペアをハッシュ表 t に登録し,その通し番号を返す */ int enter(HashTablePtr t, char *key, int value) { HashNodePtr n = NULL, m = NULL; int index; index = hash(key) % t->size; /* 内部配列の添え字を計算 */ /* キーが既に存在しているかどうかチェック(lookup() と同じ処理)*/ n = t->heads[index]; while (n != NULL) { if (strcmp(key, n->key) == 0) return n->id; /* 通し番号を返す */ n = n->next; } /* 新しいノードを生成 */ m = malloc(sizeof(HashNode)); m->key = _strdup(key); m->id = t->serial_id; m->value = value; /* 連結リストの add_first() と同様にして連結リストに追加 */ m->next = t->heads[index]; t->heads[index] = m; t->serial_id++; /* 次の通し番号に更新 */ return m->id; /* 登録したキーと値のペアに付与された通し番号を返す */ } /* * ハッシュ表 t に登録されるキーの配列を返す *(この配列のサイズは get_cardinality() で取得可能) */ char **get_keys(HashTablePtr t) { char **keys = NULL; HashNodePtr n = NULL; int i; keys = malloc(sizeof(char *) * t->serial_id); /* 各連結リストを走査し,配列に詰め込む */ for (i = 0; i < t->size; i++) { n = t->heads[i]; while (n != NULL) { keys[n->id] = n->key; /* 通し番号を配列添え字に */ n = n->next; } } return keys; /* 後で free() する必要あり */ } void print_hashtable(HashTablePtr t) { printf("Taro => %d\n", lookup(t, "Taro")); } /* ハッシュ表の使用例 */ int main(void) { HashTablePtr t = NULL; t = create_hashtable(); enter(t, "Taro", 25); print_hashtable(t); enter(t, "Taro", 35); print_hashtable(t); delete_hashtable(t); }