
http://www.bohyoh.com/Books/CalgoA/EX/ALGOEX0901 …
上の線形リストのプログラムにおいて、着目ノードを1個次にずらす操作と、1個前にずらす操作を追加したいのですが、単連結リストでは後者の実現は少しばかりややこしく、困っています。
おそらく先頭ノードから後続ノードへのポインタを辿って行って、着目ノードを探索すれば良いのでしょうが……。
尚、前者において着目ノードが末尾ノードだった場合、後者において着目ノードが先頭ノードだった場合は考えないものとします。
皆様にご教授頂けると幸いです。
/* 着目ノードを一個次のノードにする */
void NextCrnt(List *list)
{
if (list->crnt == NULL){
puts("着目要素はありません。");
}
else {
list->crnt = list->crnt->next;
}
}
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消滅 (謎)
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/ …
辺りを参照していただくとして
下のサンプルを仮に直すとしたら
typedef struct Node Node, *PNODE;
struct Node {
PNODE next;
int num;
};
ぐらいでよさそうって感じですね。(もっとも、10文字以内程度の差ぐらいですが)
なお、ポインタのポインタについてですが
もうちょっと詳しく言うと
普通に全体に特定の処理かけるだけとかなら、単なるポインタの方が簡単だし、表記上も処理上も無駄がない可能性高いですが
「挿入や削除、追加などをはじめとする、付け替えの時」にポインタのポインタ使うと非常にすっきりとして、便利な可能性が高いです。
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++も読み書きできるようになる」ってことはやはりとてもお勧めですね。
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つとか、ポインタポインタで十分解決できると思います。
具体的にはどういう用途がお望みですか?
回答ありがとうございます。
用途は特に想定していません。こういった場合双連結リストを使うべきなのは承知しておりますが、ひねくれた性分でして…。
授業で習ったプログラムをどうにかして弄くり回そうと、いろいろやってみたくなるんです。まぁ、テスト勉強も兼ねていますが。
ご提案いただいた方法でも試してみますね。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- 大学・短大 C言語線形リストの問題です 3 2022/12/22 00:45
- XML マスターノード 1 2023/03/14 10:38
- Visual Basic(VBA) ユーザーフォームの表示を追加したい 2 2023/03/26 23:18
- その他(プログラミング・Web制作) pythonのmap、結果の利用は1度だけ? 5 2022/06/11 12:33
- PHP style.cssのjQuery条件付きcssが機能しない 4 2022/07/17 18:27
- その他(プログラミング・Web制作) このプログラミングをどう組みますか? Googlecolabでやってるんですが、出来る方お願いします 1 2022/07/13 10:52
- PHP PHPでCSVを出力するさいに、ループの中で前の行の値を変更したい 3 2022/10/27 17:44
- PHP PHPの構文で間違えが分からない 5 2022/07/11 16:38
- Visual Basic(VBA) ユーザーフォーム「frm_基本❶」を立ち上げると新規で入力する行数を右下のNoとして表示しています。 1 2023/03/16 19:02
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C言語特有の文法や概念について
-
ポインタのsizeofについて
-
ポインタ定義は必要なんですか?
-
マクロ,クラス
-
ファイルポインタのヘッダーフ...
-
C言語のvoid型ポインタ変数につ...
-
連結リスト 要素の入れ替え
-
配列3個へのポインタを返す関数
-
[C言語] NULLは必ず0(番地)です...
-
ポインタのミスでOS壊れるの...
-
structをポインタ宣言時の領域
-
C言語の練習
-
sprintf 初歩的な質問
-
gccでMAKEINTRESOURCEするとdif...
-
init関数の意味
-
関数のパラメタ(C++)
-
戻り値で構造体を返すことは可...
-
引数のポインタ変数をローカル...
-
プログラミング C++
-
#include <stdio.h> #include <...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
セグメントエラー
-
C言語のポインタに直接アドレス...
-
init関数の意味
-
Run-Time Check Failure #3とい...
-
戻り値で構造体を返すことは可...
-
ExcelVBAでのkernel32(64bit)
-
アプリを32bitから64bit移行
-
参照型で受け取った引数をポイ...
-
fopne で失敗する原因
-
PASCALとFARの意味
-
LPSTR型の初期化について
-
CWnd::EnableWindow()の扱い方
-
ポインタについて
-
プーさんのマウスポインタを教...
-
連結リスト 要素の入れ替え
-
ハンドルはポインタか
-
C++で関数ポインタから関数名を...
-
自作DLLの引数について、ポイン...
-
NULLポインタが0でない処理系と...
-
TCHAR文字列内の検索について
おすすめ情報