ページ 11

線形リストのソート

Posted: 2007年7月12日(木) 05:19
by マルクス
はじめまして。今回教えてほしいことなのですがリストをつくってランク順に並べるという方法なのですが
リストを作成してソートする方法はできるのですがノードを作成して適切な場所に挿入する方法がいまいち
わかりません;;
一応ソースはこのようになりました。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct List {
char Number[10];
char rank;
struct List *next;
} rankNode;


/*
makeNewNode()関数:新しいノードを作成する関数
*/
rankNode *NewNode( void ) {
rankNode *newNode;

newNode = (rankNode *)malloc( sizeof( rankNode ) );
newNode->next = NULL;
return( newNode );
}

/*
insertNext()関数:引数で渡されたノードの後ろに新しいノードを追加する
*/
void insertNext( rankNode *curNode, char *pId, char cGrade ) {
rankNode *newNode;

newNode = NewNode();

/* 新しいノードにデータをコピー */
strcpy( newNode->Number, pId );
newNode->rank = cGrade;

newNode->next = curNode->next;
curNode->next = newNode;
}

/*
insertFirst()関数:最初のノードの前に新しいノードを追加する
*/
rankNode *insertFirst( rankNode *firstNode, char *pId, char cGrade ) {
rankNode *newNode;

newNode = NewNode();

/* 新しいノードにデータをコピー */
strcpy( newNode->Number, pId );
newNode->rank = cGrade;

newNode->next = firstNode;
firstNode = newNode;

return( firstNode );
}


int main( void ) {
int i;
char Number[10];
char rank;
FILE *pFile;

rankNode *pTop;
rankNode *pNow;

/* データファイルを開く */
pFile = fopen( "rank.txt", "r" );
if( pFile == NULL ) {
printf( "Error!!! \n" );
return( 2 );
}

pTop = NULL;

/* ファイルの終わりまで読み込み,リストの最初に追加する */
while( fscanf( pFile, "%s %c", Number, &rank ) != EOF ) {
/* ノードがない場合 */
if( pTop == NULL ) {
pTop = insertFirst( pTop, Number, rank );
}
/* ノードが1つの場合 */
else if( pTop->next == NULL ) {
if( pTop->rank > rank ) {
pTop = insertFirst( pTop, Number, rank );
}
else{
insertNext( pTop, Number, rank );
}
}
/* ノードが2つ以上の場合 */
else {

}
}

return( 0 );
}
ノードがないときと1つの時はできたのですが
ノードを2つ以上ある時の挿入方法がいまいちわからないのです。
ちなみにrank.txtの内容はこんなかんじです。
番号  ランク
001 C
002 A
003 B
004 C
005 D
よろしくお願いします。

Re:線形リストのソート

Posted: 2007年7月12日(木) 18:37
by box
同一ランク(今回の場合はC)の場合にどういう並び順に
したいかを含めて、希望されている結果を提示してください。

なお、ダミーノードの考え方を使うと、リスト構造のどこに
挿入するかを考えるのが多少楽になります。
先頭かどうかを意識する必要性が減るか、あるいは
全く意識しなくてよくなりますので。

Re:線形リストのソート

Posted: 2007年7月12日(木) 21:03
by マルクス
返信ありがとうございます@@
少し説明不足でしたが結果は
002 A
003 B
001 C
004 C
005 D
のように同ランクの場合は後から追加したものを
先に追加した同ランクの後ろについかします。

ダミーノードという考えは初めて?聞きました。
すこし調べてきます・・・・orz

Re:線形リストのソート

Posted: 2007年7月12日(木) 21:36
by box
リスト構造について説明しているサイトはたくさんありますが、
私が知っている中で比較的わかりやすかったところを挙げておきます。
よかったら行ってみてください。

http://tdweb.cssa.chs.nihon-u.ac.jp/ds/ds03.html
http://tdweb.cssa.chs.nihon-u.ac.jp/ds/ds04.html

ds04の方で、ダミーノードについて説明しています。

Re:線形リストのソート

Posted: 2007年7月16日(月) 03:11
by マルクス
返信遅くなりました。結果を表示する関数などを追加して新たにつくってみたのですがうまくいきません。
ちなみにこれがソースです。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct List {
char Number[10];
char rank;
struct List *next;
} rankNode;


/*
makeNewNode()関数:新しいノードを作成する関数
*/
rankNode *NewNode( void ) {
rankNode *newNode;

newNode = (rankNode *)malloc( sizeof( rankNode ) );
newNode->next = NULL;
return( newNode );
}

/*
insertNext()関数:引数で渡されたノードの後ろに新しいノードを追加する
*/
void insertNext( rankNode *curNode, char *pId, char cGrade ) {
rankNode *newNode;

newNode = NewNode();

/* 新しいノードにデータをコピー */
strcpy( newNode->Number, pId );
newNode->rank = cGrade;

newNode->next = curNode->next;
curNode->next = newNode;
}

/*
insertFirst()関数:最初のノードの前に新しいノードを追加する
*/
rankNode *insertFirst( rankNode *firstNode, char *pId, char cGrade ) {
rankNode *newNode;

newNode = NewNode();

/* 新しいノードにデータをコピー */
strcpy( newNode->Number, pId );
newNode->rank = cGrade;

newNode->next = firstNode;
firstNode = newNode;

return( firstNode );
}

/*
traverseList()関数:リストを走査する
引数
gradeNode *firstNode:最初のノードのアドレス
返値
なし
*/
void printfList( rankNode *firstNode ) {
rankNode *nowNode;

/* 最初のノードを見るようにする */
nowNode = firstNode;

/* 現在見ているノードがNULL(リストの最後)になるまで繰り返す */
while( nowNode != NULL ) {

printf( "Number=%s, rank=%c, \n",
nowNode->Number, nowNode->rank);

nowNode = nowNode->next; /* 次のノードを見るようにする */
}
}

int main( void ) {
int i;
char Number[10];
char rank;
FILE *pFile;

rankNode *pTop;
rankNode *pNow;

/* データファイルを開く */
pFile = fopen( "rank.txt", "r" );
if( pFile == NULL ) {
printf( "Error!!! \n" );
return( 2 );
}

pTop = NULL;

/* ファイルの終わりまで読み込み,リストの最初に追加する */
while( fscanf( pFile, "%s %c", Number, &rank ) != EOF ) {
/* ノードがない場合 */
if( pTop == NULL ) {
pTop = insertFirst( pTop, Number, rank );
}
/* ノードが1つの場合 */
else if( pTop->next == NULL ) {
if( pTop->rank > rank ) {
pTop = insertFirst( pTop, Number, rank );
}
else{
insertNext( pTop, Number, rank );
}
}
/* ノードが2つ以上の場合 */
else {
pNow=pTop;
if( pTop->rank > rank )
pTop=insertFirst( pTop, Number, rank);
else if( pTop->rank == rank){
while(pNow!=NULL){
if(pNow->next!=NULL && pNow->next->rank==rank)
pNow=pNow->next;
else break;
}
insertNext(pNow,Number,rank);
pNow=pTop;
}
else
while(pNow!=NULL){
if(pNow->next!=NULL && pNow->next->rank<=rank)
pNow=pNow->next;
else break;
}
insertNext(pNow,Number,rank);
pNow=pTop;
}
}
printfList(pTop);
return( 0 );
}

それでrank.txtの中身は下でしめしますが
001 C
002 A
003 B
004 C
005 D
006 B
007 A
008 D
です。
しかし表示が
Number=002, rank=A
Number=007, rank=A
Number=007, rank=A
Number=003, rank=B
Number=006, rank=B
Number=001, rank=C
Number=004, rank=C
Number=005, rank=D
Number=008, rank=D
となってしまいます。
なぜか重複?するのがでてきてしまいます。。。。(Number007が2つ。。。orz)
どこがおかしいのかお分かりでしたら教えてください、お願いします<(__)>

Re:線形リストのソート

Posted: 2007年7月16日(月) 08:40
by box
main()で、ノードが2つ以上の場合の処理に問題があります。

else
	while(pNow!=NULL){
		if(pNow->next!=NULL && pNow->next->rank<=rank)
			pNow=pNow->next;
		else break;
	}
	insertNext(pNow,Number,rank);
	pNow=pTop;

このコードだと、else句の範囲がwhile文によるループまでとなり、
insertNext()とその次の文は必ず実行してしまいます。
No.007をリストに2回登録したのは、これが原因だと思います。

こんな風に修正する必要があります。

else {
	while(pNow!=NULL){
		if(pNow->next!=NULL && pNow->next->rank<=rank)
			pNow=pNow->next;
		else break;
	}
	insertNext(pNow,Number,rank);
	pNow=pTop;
}

これで、とりあえず正しい結果を得るようになるはずです。

また、以下の点も修正する方がよいと思います。
  ・同じ処理を複数回書いている箇所があるが、冗長である
  ・malloc()に失敗した場合のことを考慮する
  ・malloc()した領域が不要になったら、free()する
  ・使っていない変数の定義は削除する

Re:線形リストのソート

Posted: 2007年7月17日(火) 19:42
by マルクス
返信遅くなりました;;
確かにそのようにやるとできました!
修正する点もなんとか改良してできました^^
boxさんありがとうございます!