4

こんにちは、リンク リストの練習をしようとしています。

というオブジェクトクラスを定義しましたStudent

public class Student
{
      protected string  Student_Name;
      protected int Student_ID;
      protected int Student_Mark;
      protected char    Student_Grade;

      public Student()  // default Constructor
      {
         Student_Name = "           ";
         Student_ID = 0;
         Student_Mark = 0;
         Student_Grade = ' ';
        }

      public Student(string Sname, int Sid, int Smark, char Sgrade) // Constructor
      {
         int len = sname.Length;
         Student_Name = sname.Substring(0, len);
         //Student_Name = sname.Substring(0, sname.Length);
         Student_ID = Sid;
         Student_Mark = Smark;
         Student_Grade = Sgrade;
      }
}

そしてNodeクラス:

public class S_Node
{
      public Student    Element;
      public S_Node Link;

      public S_Node()
      {
         Element = new Student();
         Link = null;
      }

      public Node(Student theElement)
      {
         Element = theElement;
         Link = null;
      }
}

そしてLinkedList

public class S_LinkedList
{
    protected S_Node header;
    protected S_Node tail;

    public S_LinkedList()
    {
       header = new S_Node();
       Tail = new S_Node();
       header.Link = Tail;
    }

    // METHODS which i don't know how to do it (never use linkedlist before)
}

「linkedlist データ構造タイプ」を使用して、このデータを整理する必要があります。

私が学んだリストへのノードの追加 -->(挿入),私が学んだリストからのノードの削除 -->((削除),私が学んだリストのトラバース - ->((PrintList),Finding a node in the list as I've Learned -->((Find , FindPrevious) 問題は私が自己学習していて、ネットを検索して、愚かな C# から詳細を読み取ろうとしました。やりすぎてどうしたらいいのかわからないくらい悲しいです。

このクラスを使用して実行可能プログラムを作成し、それをテストしようと懸命に努力しています。

このプログラムを完了するのを手伝いたくない場合は (願わくば) 少なくともいくつかの実際の例やアイデアを見せてください。

4

8 に答える 8

14
リストの先頭。
 ( アイテム1
   要素: 学生1
   次へ------------> ( item2
  ) 要素: Student2
                        次へ------------> ( item3
                      ) 要素: Student3
                                             次へ: null
                                           )
                                           リストの末尾。

まず、StudentList クラスを作成できるようにするには、最初にクライアント コードを作成する必要があります。クライアント コードは、学生リストを使用するコードです。また、一度に1つのことだけを書いて捨てないでください。代わりに、StudentList を操作するために必要なさまざまな方法を実行する [テスト] ケースを大量に作成します。例外的なケースも書きます。しかし、できるという理由だけですべてを行うクラスのスイスアーミーナイフを書きたがる誘惑に駆られないでください。作業を完了するための最小限のコードを記述します。

クラスをどのように使用する必要があるかによって、クラスの構築方法が大きく決まります。これがTDDまたはテスト駆動設計の本質です。

私が見ることができるあなたの最大の問題は、クラスをどのように使用したいのかわからないことです。では、まずそうしましょう。

// create a list of students and print them back out.
StudentList list = new StudentList();
list.Add( new Student("Bob", 1234, 2, 'A') );
list.Add( new Student("Mary", 2345, 4, 'C') );

foreach( Student student in list)
{
    Console.WriteLine(student.Name);
}

生徒をリストに追加してから、それらを印刷します。

クライアント コードで StudentList の内部を確認する必要はありません。したがって、StudentList はリンク リストの実装方法を隠します。StudentList の基本を書きましょう。

public class StudentList 
{
    private ListNode _firstElement; // always need to keep track of the head.

    private class ListNode
    {
        public Student Element { get; set; }
        public ListNode Next { get; set; }
    }

    public void Add(Student student) { /* TODO */ }

}

StudentList はかなり基本的なものです。内部的には、最初のノードまたはヘッド ノードを追跡します。最初のノードを追跡することは、明らかに常に必要です。

また、なぜ ListNode が StudentList の中で宣言されているのか疑問に思うかもしれません。何が起こるかというと、ListNode クラスは StudentList クラスからのみアクセス可能です。これは、リストへのすべてのアクセスを制御しているため、StudentList が内部実装に詳細を提供したくないために行われます。StudentList は、リストがどのように実装されているかを決して明らかにしません。実装の隠蔽は、OO の重要な概念です。

クライアント コードがリストを直接操作できるようにした場合、StudentList を最初に配置しても意味がありません。

Add() 操作を実装してみましょう。

public void Add(Student student)
{
    if (student == null)
        throw new ArgumentNullException("student");

    // create the new element
    ListNode insert = new ListNode() { Element = student };

    if( _firstElement == null )
    {
        _firstElement = insert;
        return;
    }

    ListNode current = _firstElement;
    while (current.Next != null)
    {
        current = current.Next;
    }

    current.Next = insert;
}

Add 操作では、リスト内の最後の項目を見つけて、新しい ListNode を最後に配置する必要があります。しかし、ひどく効率的ではありません。現在は O(N) であり、リストが長くなるにつれて追加が遅くなります。

これを挿入用に少し最適化し、Add メソッドを書き直します。Add を高速化するには、StudentList にリストの最後の要素を追跡させるだけです。

private ListNode _lastElement;  // keep track of the last element: Adding is O(1) instead of O(n)

public void Add(Student student)
{
    if( student == null )
        throw new ArgumentNullException("student");

    // create the new element
    ListNode insert = new ListNode() { Element = student };

    if (_firstElement == null)
    {
        _firstElement = insert;
        _lastElement = insert;
        return;
    }

    // fix up Next reference
    ListNode last = _lastElement;
    last.Next = insert;
    _lastElement = insert;
}

今、追加するとき、反復しません。head と tail の参照を追跡する必要があるだけです。

次は foreach ループです。StudentList はコレクションであり、それを列挙して C# の を使用したいコレクションですforeach。C# コンパイラは魔法のように繰り返すことはできません。foreach ループを使用するには、記述したコードが明示的に列挙子を使用していないように見える場合でも、使用する列挙子をコンパイラに提供する必要があります。

最初に、リンクされたリストを反復処理する方法をもう一度見てみましょう。

// don't add this to StudentList
void IterateOverList( ListNode current )
{
    while (current != null)
    {
        current = current.Next;
    }
}

わかった。それでは、C# の foreach ループにフックして、列挙子を返しましょう。そのために、StudentList を変更して IEnumerable を実装します。これは少し進んでいますが、何が起こっているのかを理解できるはずです。

// StudentList now implements IEnumerable<Student>
public class StudentList : IEnumerable<Student>
{
    // previous code omitted

    #region IEnumerable<Student> Members
    public IEnumerator<Student> GetEnumerator()
    {
        ListNode current = _firstElement;

        while (current != null)
        {
            yield return current.Element;
            current = current.Next;
        }
    }
    #endregion

    #region IEnumerable Members
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    #endregion
}

そこにリンクされたリストの反復を見つけることができるはずです。yieldキーワードに惑わされないでください。yield が行っているのは、現在の生徒を foreach ループに戻すことだけです。列挙子は、リンクされたリストの最後に到達すると、学生の返却を停止します。

以上です!コードは思い通りに機能します。


* リストを実装する方法はこれだけではありません。リスト ロジックを StudentList に配置し、ListNode を非常に基本的なものにすることにしました。しかし、コードは最初の単体テストで必要なことだけを行い、それ以上のことはしません。実行できる最適化は他にもあり、リストを作成する方法は他にもあります。

今後: まず、コードに必要な [単体] テストを作成し、次に必要な実装を追加する必要があります。


* fyi Student クラスも書き直しました。あなたが提供したコードがコンパイルされないことは言うまでもありません。_私はプライベート メンバー変数よりもリーダーとしてを好みます。それを好まない人もいますが、あなたはこれに慣れていないので、簡単に見つけられるのでそのままにしておきます.

public class Student
{
    private string _name;
    private int _id;
    private int _mark;
    private char _letterGrade;

    private Student()  // hide default Constructor
    { }

    public Student(string name, int id, int mark, char letterGrade) // Constructor
    {
        if( string.IsNullOrEmpty(name) )
            throw new ArgumentNullException("name");
        if( id <= 0 )
            throw new ArgumentOutOfRangeException("id");

        _name = name;
        _id = id;
        _mark = mark;
        _letterGrade = letterGrade;
    }
    // read-only properties - compressed to 1 line for SO answer.
    public string Name { get { return _name; } }
    public int Id { get { return _id; } }
    public int Mark { get { return _mark; } }
    public char LetterGrade { get { return _letterGrade; } }
}
  • パラメータをチェック
  • プロパティ、クラス、および変数の大文字と小文字の違いに注意してください。
  • デフォルトのコンストラクターを非表示にします。実際のデータなしで学生を作成したいのはなぜですか?
  • いくつかの読み取り専用プロパティを提供します。
    • このクラスは、書かれているように不変です (つまり、学生を作成すると、それを変更することはできません)。
于 2008-10-30T00:18:44.980 に答える
2

私はあなたのために 1 つやります !各ノードをボックスとして図を描き、各操作のリストを変更するためにどのコードを使用する必要があるかを理解する必要があります。インスピレーションについては、これを参照してください。

http://en.wikipedia.org/wiki/Linked_list

そこにある図では、メイン リスト クラスがボックスとして表示されていません。これは、ヘッダーとテールの 2 つの矢印がそこから出ている必要があります。

Insert メソッドの 2 つのケースの図をいくつか描いて、何が起こっているのかを理解してください。リストに何もなく、ヘッダーが null の場合の 1 つの図と、リストに既に何かがある場合の別の図。そこから、他の操作を実行します。

public class S_LinkedList {

    protected S_Node header = null;

    protected S_Node tail = null;

    public S_LinkedList()
    {
    }

    // METHODS which i don't know how to do it (never use linkedlist before)
    void Insert(Student s)
    {
        if( header == null )
        {
            header = new S_Node(s);
            tail = header;
        }
        else
        {
            tail.Link = new S_Node(s);
            tail = tail.Link;
        }
    }
}
于 2008-10-29T19:30:17.560 に答える
2

不足している操作は次のとおりです。

追加:

尾ノードのリンクを追加ノードに設定し、尾ノードを新しいノードに設定します。

削除/削除:

二重にリンクされたリストがない場合は少し注意が必要ですが、単一にリンクされたリストでは、前のノードを別の変数に保持する必要があるノードが見つかるまで、リストを先頭から調べます。削除するノードが見つかったら、前のノードのリンクをそのノード リンクに設定します。最適化は、探しているリンクではないことを確認することです。または、二重にリンクされたリストにすると、前のノードを追跡する必要がなくなります。

探す:

探しているものが見つかるまで、ノードからノードへとリストをトラバースします。

詳細については、このウィキペディアの記事をお読みください。

于 2008-10-29T19:30:28.543 に答える
2

これを見て...

これを行う方法を本当に学びたい場合は、少なくとも c または c++ で書く必要がありますが、そうすれば何か役に立つことになります...

于 2008-10-29T19:37:08.810 に答える
1

リンクリストの概念を理解するのはそれほど難しくありません。一方、実装は少し注意が必要です。

それについての情報をウェブ上で見つけようとするあなたの欲求不満も理解できます。私は以前あなたのボートに乗ったことがあり、すべてがサイトごとに異なります。あなたが見つけた情報は、実際に見つけたほとんどの情報よりもはるかに明確で役立つと思うので、あなたは本当にデータ構造の本に投資したいかもしれません。

これまでllを使用したことがない場合は、Java / C#でリンクリストを実装する方がはるかに簡単です。ただし、それをよりよく理解すると、C / C ++で作成することにより、llをよりよく理解できるようになります。

上記のコードから、各S_NodeをStudentノードとして考えるのではなく、Studentオブジェクトを含む単なる通常のノードとして考える方がよいでしょう(それが理にかなっていることを願っています)。S_LinkedListクラスにも同じルールが適用されます。リンクリストは、ノードのリストモードアップです。これらのノードにはStudentオブジェクトが含まれています。

お役に立てれば。

于 2008-10-29T20:16:23.987 に答える
1

これを生徒のクラスとして試してください。

public class Student
{
    protected string Name;
    protected int ID;
    protected int Mark;
    protected char Grade;

    public Student()  // default Constructor
    {
        Name = "";
        ID = 0;
        Mark = 0;
        Grade = '';
    }

    public Student(string Name, int ID, int Mark, char Grade) // Constructor
    {
        this.Name = Name;
        this.ID = ID;
        this.Mark = Mark;
        this.Grade = Grade;
    }
}
于 2008-10-29T20:22:05.567 に答える
1

実際には、.NET には既に LinkedList ジェネリック クラスが含まれているため、C# で独自のリンク リストを作成する理由はわかりません (それがどのように機能するかを学習する以外に)。

于 2008-10-29T19:13:13.397 に答える
0

私が読んだあなたの質問は漠然としすぎています。「リンクされたリスト」をグーグルで検索するか、「データ構造」に関する本を手に取ることから始めます。特定の問題に遭遇した場合は、ここで質問してください。誰かがあなたを助けてくれると確信しています.

于 2008-10-29T19:14:26.407 に答える