C言語関係掲示板

過去ログ

No.1157 リストの入力、出力、逆出力、検索

[戻る] [ホームページ]
No.15010

リスト構造
投稿者---パピオン(2004/06/26 15:25:53)


 過去ログを見たのですが、理解できなくて書き込みをしました。
・画面からデータを入力する
・書き込んだ順に出力
・逆にして出力
・検索
をしたいのですが、表示が一件目しかされないのと検索がうまくいきません。どなたかご教授お願いします。

#include <stdio.h>
#include <math.h>
typedef struct list{ char data[20];
            struct list *next;}list;
void append(char *g);
void display(list *p);
list *found(list *p,char *x);
void rprint(list *p);
list *head, *pt;

/*----------------------------------------------------*/
int main(void)
{
    /*変数の宣言*/
    char goods[10];
    int i=0;
    
    /*ポインタの初期化*/
    head = pt = NULL;
    
    /*1件目のデータの入力*/
    printf("data(%d):",i++);scanf("%s",goods);
    
    /*2件目以降のデータの入力*/
    while(strcmp(goods,"end")!= 0){
        append(goods);
        printf("data(%d):",i++);scanf("%s",goods);
    }
    
    /*古い順の出力*/
    puts("\n古い順の出力");
    display(head);
    
    /*新しい順の出力*/
    puts("\n新しい順の出力");
    rprint(head);
    
    /*検索*/
    printf("\nお探しは");scanf("%s",goods);
    if(found(head,goods)!=NULL){
        puts("あります");
    }
    else{
        puts("ありません");
    }
    return 0;
}
/*----------------------------------------------------*/
void display(list *p)
{
    while(p!=NULL){
        printf("%s->",p->data);
        p=p->next;
    }
    return;
}
/*----------------------------------------------------*/
list *found(list *p,char *x){
    while(p!=NULL && x==NULL){
        p=p->next;
    }
    return p;
}
/*----------------------------------------------------*/
void rprint(list *p)
{
    if(p!=NULL){
        p=pt;
        printf("%s->",p->data);
    }
    return;
}
/*----------------------------------------------------*/
void append(char *g)
{
    list *p;

    /*入力されたデータの比較*/
    if((p=(list *)malloc(sizeof(list)))==NULL){
        printf("memory over !\n");exit(0);}

    /*gをdataに代入*/
    strcpy(p->data,g);

    /*次ポインタの初期化*/
    p->next = NULL;

    /*次ポインタの設定*/
    if(pt == NULL){
        head = p;
    }
    else{
        p->next = p;
    }
    pt = p;
    return;
}
/*----------------------------------------------------*/



自分的には
>printf(&quot;%s-&gt;&quot;,p-&gt;data);
>p=p-&gt;next;

>while(p!=NULL &amp;&amp; x==NULL){
の部分と
>p-&gt;next = p;
がおかしいかなと考えています。
ただどうおかしいのかがよくわかりません。

よろしくおねがいします。


No.15012

Re:リスト構造
投稿者---RAPT(2004/06/26 21:28:40)


Windows2000sp4/VC++6.0sp6/Console-Appでコードを書いたのですが、
そのまま掲載するよりもアドバイス的なものの方が役に立つと思ったので
質問形式で書いてみました。個々の質問に回答をつけるように自分のコード
を再確認してみてください。

math.h は不要かと。それよりも、stdlib.h と string.h がない方が変。

> 表示が一件目しかされない
display() 関数と、rprint() 関数はその処理の目的から、ほぼ似たような
処理になるはずですが、この2つの関数が全然似ていませんよね?

そもそも、データの構造的に、単方向リストでは厳しいと思います。双方向
リストを構築すれば、逆順表示は楽にできますが、単方向リストでないと
ならない、といった制限があるのでしょうか?

/*次ポインタの設定*/ で、else 節の p->next = p; がおかしいです。
このままだと無限ループな上、リストの先頭と次以降が紐づきませんよね?

found() 関数では、文字列と一致するノードを探すはずなのに、文字列同士
の比較判定処理がないので、意味無いですよね? せっかく、入力処理で
入力終了の確認を文字列比較で可能にしているのに…。

全体的に、グローバル変数の使い方が変です。統一性を持たせるか、いっそ
グローバル変数を使わないようにすると、いいでしょう。

最後に、malloc()で確保したメモリは、必ずfree()で開放しましょう。



No.15013

Re:リスト構造
投稿者---パピオン(2004/06/27 00:01:16)


返答ありがとうございます。

>> 表示が一件目しかされない
>display() 関数と、rprint() 関数はその処理の目的から、ほぼ似たよう
>な処理になるはずですが、この2つの関数が全然似ていませんよね?
 確かにそのとおりですね。もう一度考え直してみます。

>そもそも、データの構造的に、単方向リストでは厳しいと思います。双
>方.向リストを構築すれば、逆順表示は楽にできますが、単方向リストで
>ないとならない、といった制限があるのでしょうか?
 単方向リストでは不可能なのでしょうか?
制限はありませんが、ここまで着たらやってみたいと思っています。

>/*次ポインタの設定*/ で、else 節の p->next = p; がおかしいです。
>このままだと無限ループな上、リストの先頭と次以降が紐づきませんよね?
 はい、無限ループになってしまいました。どうやったらいいのかわからなくて。pをnextに入れないと次のデータを探せないかと思いまして。もう一度考え直してみます。

>found() 関数では、文字列と一致するノードを探すはずなのに、文字列同
>士の比較判定処理がないので、意味無いですよね?せっかく、入力処理
>で入力終了の確認を文字列比較で可能にしているのに…。
 はい。NULLをいれて無理やり通してるだけになってました。
strcmp(x,p->data)!= 0
とすることで解決できました。

>全体的に、グローバル変数の使い方が変です。統一性を持たせるか、いっ
>そグローバル変数を使わないようにすると、いいでしょう。
 はい、変数・ポインタの使い方がまだまだ理解できていなくて、勉強中です。統一性をもたせたいと思っているのですが・・・

 返事遅れてすみません。初心者でなにかと失礼をするかもしれませんがよろしくお願いします。

</pre>



No.15014

Re:リスト構造
投稿者---パピオン(2004/06/27 00:40:20)


RAPTさんのご指摘のおかげで逆に表示する以外は解決できました。

単方向リストでは逆にすることは難しいですね。前を示すものがないですし・・・

何かよい方法はないでしょうか?


No.15015

Re:リスト構造
投稿者---あかま(2004/06/27 02:33:49)


表示するだけなら再帰にすればいいのではないでしょうか。
void rev_display(list *p)
{
    if(p!=NULL){
        rev_display(p->next);
        printf("%s->",p->data);
    }
    
    return;
}



No.15016

Re:リスト構造
投稿者---RAPT(2004/06/27 08:43:16)


あかまさんの再帰を使って書き直してみました。
Windows2000sp4/VC++6.0sp6/Console-App にて動作確認済。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define DATA_SIZE  20

/* リストの定義 */
typedef struct list{
  char data[1 + DATA_SIZE];
  struct list *next;
}list;

/* 広域変数宣言 */
list *g_head, *g_tail;

/* ノードの追加 */
void append(const char *str)
{
  list *p = NULL;

  /* メモリの確保 */
  if((p=(list *)malloc(sizeof(list)))==NULL){
    printf("memory over !\n");
    exit(0);
  }

  /* データのコピー */
  strncpy(p->data, str, DATA_SIZE);
  p->next = NULL;

  /* ノードのリンク */
  if(g_head == NULL){
    g_head = g_tail = p;
  }else{
    g_tail->next = p;
    g_tail = p;
  }
}

/* 先頭から表示 */
void display(list *p)
{
  while(p != NULL){
    printf("%s->", p->data);
    p = p->next;
  }
}

/* 最後から表示 */
void rev_display(list *p)
{
    if(p != NULL){
        rev_display(p->next);
        printf("%s->", p->data);
    }
}

/* 検索 */
list *find(list *p, const char *str){
  if( str == NULL || strlen(str) < 1 ){
    return NULL;
  }
  for(; p && strcmp(p->data, str) != 0; p = p->next);
  return p;
}

/* メモリの開放 */
void term(list *p)
{
  list *begin = p;
  while( begin ){
    p = begin->next;
    free(begin);
    begin = p;
  }
}

int main()
{
  char goods[1 + DATA_SIZE];
  int i = 0;

  for(;;){
    printf("data(%d):", i++);
    scanf("%s", goods);
    if(strcmp(goods, "end") == 0){
      break;
    }
    append(goods);
  }

  puts("\n古い順の出力");
  display(g_head);

  puts("\n新しい順の出力");
  rev_display(g_head);

  printf("\nお探しは:");
  scanf("%s", goods);
  if(find(g_head, goods) != NULL){
    puts("あります");
  }else{
    puts("ありません");
  }

  term(g_head);

  return 0;
}



No.15022

Re:リスト構造<解決>
投稿者---パピオン(2004/06/27 23:01:33)


 ありがとうございました。皆さんのおかげで解決できました。
ただ、データの引渡しがうまく理解できていないのでもう一度勉強し直してみようと思います。
(他にもまだまだわからないところだらけですが(^^ゞ)

 いつかは回答する側になれるよう努力しようと思います。

 本当にありがとうございました。