リンクリストノードの削除

このコードは、リンクリスト内の奇数値のノードを削除するためのものです。これはうまくいきますが、読みやすくするために微調整したいと思います。改善を提案してください。

public class MyLinkedList
{
    public int data;
    public MyLinkedList next;

    public MyLinkedList(int v)
    {
        this.data = v;
    }

    //public void add(int i)
    //{
   //   this.next = new MyLinkedList(i);
   //   this.next.next = null;
    //}

    public void printLL()
    {
        MyLinkedList mmm = this;
        while (mmm != null)
        {
            Console.Write(mmm.data);
            Console.Write("->");
            mmm = mmm.next;
        }
    }
}

public static void deleteOdd(ref MyLinkedList mll)
{
    MyLinkedList head, previous, curr;
    head = mll;
    while (head.data % 2 != 0)
    {
        head = head.next;
    }
    mll = head;

    previous = head;
    curr = previous.next;

    while (curr != null)
    {
        if (curr.data % 2 != 0)
        {
            previous.next = curr.next;
            curr = previous.next;
        }
        else
        {
            previous = previous.next;
            curr = curr.next;
        }
    }
}

static void Main(string[] args)
    {
        MyLinkedList myll1 = new MyLinkedList(1); MyLinkedList myll2 = new MyLinkedList(3);
        MyLinkedList myll3 = new MyLinkedList(4); MyLinkedList myll4 = new MyLinkedList(4);
        MyLinkedList myll5 = new MyLinkedList(5); MyLinkedList myll6 = new MyLinkedList(6);
        MyLinkedList myll7 = new MyLinkedList(5); MyLinkedList myll8 = new MyLinkedList(6);
        MyLinkedList myll9 = new MyLinkedList(6); MyLinkedList myll10 = new MyLinkedList(9);
        MyLinkedList myll11 = new MyLinkedList(11); MyLinkedList myll12 = new MyLinkedList(12);

        myll1.next = myll2; myll2.next = myll3; myll3.next = myll4; myll4.next = myll5;
        myll5.next = myll6; myll6.next = myll7; myll7.next = myll8; myll8.next = myll9;
        myll9.next = myll10; myll10.next = myll11; myll11.next = myll12; myll12.next = myll12; myll12.next = null;


    myll1.printLL();
    Console.WriteLine();
    deleteOdd(ref myll1);
    Console.WriteLine();
    myll1.printLL();
}
2
nl ru de
MyLinkedList mmm = this 意味のない変数名を使用すると事が複雑になります。それを currentNode またはそれに類似したものと呼ぶことは途方もない助けになるでしょう。
追加された 著者 Flater,

4 答え

抽象化の欠如

MyLinkedList isn't really a linked list - it's a single node. The lack of an encapsulating list class makes it difficult to provide reusable methods such as Add, Remove and Contains, and the lack of such methods (among others) makes your linked list difficult and error-prone to use.

  • MyLinkedList is a misleading name. Something like MyLinkedListNode would be more accurate.
  • Creating a proper linked list class that 'manages' its nodes allows you to create general-purpose methods like Add and Remove, and it can implement IEnumerable and other useful interfaces (this makes it usable with foreach and Linq methods). With such methods, removing odd entries will be easier. Without them, your class is practically useless.
  • Collection classes are almost always generic - this makes them usable with different data-types.
  • printLL not only ties your class to the console (which VisualMelon already pointed out), it also doesn't need to be part of the class (it's using public data), and it shouldn't be part of the class because it doesn't contribute to the core responsibilities (storing data).
  • All data in your class is public. That's often a bad idea, as it allows any code to modify it, and indeed, it's easy to create a problematic state: a.next = b; b.next = a; (try calling a.printLL() or removeOdd(ref a); on that).

If you're reinventing a wheel for educational purposes, then don't forget to study existing wheels (such as LinkedList).

その他の注意

  • The use of ref here is a hack. Consider the following situation: MyLinkedList b = a; deleteOdd(ref b); a.printLL(); - a can still contain odd values. You don't need this if you create a proper linked list class.
  • Squashing multiple statements on a single line makes code harder to read. It also doesn't solve the underlying problem, which is that this linked list is cumbersome to use. It would be easier if it offered a constructor that accepted an IEnumerable. And if it had an Add method, you could use collection initializer syntax: var myList = new MyLinkedList { 1, 3, 5 };.
4
追加された
トンピーターをありがとう!これはまさに私が知りたかったものです。私はOOPの抽象化の柱を見逃していたため、addとprintの両方の方法を混同していました。
追加された 著者 Peter,
'適切な'リンクリストインターフェースの論理的な利点を広げてくれてありがとう。
追加された 著者 VisualMelon,

簡単な解説

  • MSDNでは、命名規則をいくつか提供しています。重要なのは、 public メンバーは ProperCamelCase であるべきだということです(例: Data ではなく Data )。

  • パブリックメンバーと型は、APIの使用方法を完全に明確にするために、少なくとも最小限のインラインドキュメント( \\\ )を持つことが理想的です。

  • 古い(コメント付き)コードを削除するのが最善の方法です。それは維持されずにそこにあるコードと互換性がなくなるという習慣があります。それはまた混乱を追加します。

  • 'export'コードの場合、コンソールに明示的に書き込むメソッドがあると便利なことはめったにありません。抽象的な TextWriter に書き込むメソッドがあると非常に便利です( Console.Out または StreamReader )、または単に文字列を返します。これらは消費者にもっと多くの選択肢を与えます。

  • deleteOdd(MyLinkedListを参照)は、空のリスト(渡されたり返されたりするリスト)に対処できない可能性があります。

  • printLL はすばらしい変数名ではありません。「LL」とは何ですか?これは MyLinkedList のメンバーなので、 "LL"ビットは冗長です。印刷が左から右に行われるのか、それとも何かに関連するのであれば、しばらくの間試してみます。

  • mmm もすばらしい変数名ではありません。 が mll であることを意味していると私は思うだけです!


代替実装

Below are some methods which just about tally with yours, but using LinkedList, and incorporating some of the suggestions above.

I don't know if you have a particular reason for needing a custom LinkedList implementation (not suggested by the code), but you can implement your methods tidily with .NET's built in LinkedList, which could be argued to provide a nicer API. Crucially, it wraps up a linked list of LinkedListNodes under a LinkedList, which allows some (illusion of) separation of concerns, and spares you having to keep track of the head of the list explicitly. It also allows you to have an 'empty' list, which is good.

奇数を削除

This method is considerably shorter than before, and doesn't require a ref parameter. This works with empty lists. This is a much more understandable implementation, and much harder to 'get wrong', because of the API for LinkedList.

/// 
/// Removes odd elements from a LinkedList of integers ///
 
public static void DeleteOdd(LinkedList ll)
{
    LinkedListNode cur = ll.First;//grab first node

    while (cur != null)
    {
        var next = cur.Next;//make a note of the next node (will be null if cur is the last element)

        if (cur.Value % 2 != 0)
        {
            ll.Remove(cur);//remove the current node if odd
        }

        cur = next;//advance to the next node
    }
}

printLL

Here are two possible alternatives, which don't depend explicitly on Console: one just produces a string, the other prints to a TextWriter (sadly there is no better interface). These are both extension methods for LinkedList, but there is no particular need for this.

/// 
/// Renders a linked list as a string ///
 
public static string Render(this LinkedList ll)
{
    StringBuilder sb = new StringBuilder();

    foreach (var e in ll)
    {
        sb.Append(e.ToString());
        sb.Append("->");
    }

    return sb.ToString();
}

/// 
/// Prints a linked list to a TextWriter, optionally appending a NewLine ///
 
public static void Print(this LinkedList ll, TextWriter textWriter, bool newLine = true)
{
    foreach (var e in ll)
    {
        textWriter.Write(e.ToString());
        textWriter.Write("->");
    }

    if (newLine)
        textWriter.WriteLine();
}

私は必ずしも newLine パラメータがオプションであること、あるいは実際にそこにあることを認めないと思います。これは単にいくつかのオプションを説明するためのものです。両方が必要な場合は、おそらく PrintPrintLine の両方を使用するほうが良いでしょう。

使用例

Note the nicer API for building a LinkedList.

private static void ExampleUsage()
{
    LinkedList ll = new LinkedList();
    ll.AddLast(1);
    ll.AddLast(2);
    ll.AddLast(3);
    ll.AddLast(4);
    ll.AddLast(5);

    Console.WriteLine(ll.Render());
    ll.Print(Console.Out);
    DeleteOdd(ll);
    Console.WriteLine(ll.Render());
    ll.Print(Console.Out);
}
4
追加された
詳細な回答VisualMelonをどうもありがとう!多分私ははっきりしなかったが、私が実際に尋ねるつもりだったのは、コードをフォーマットするだけではなく、ロジックをよりエレガントに見えるように調整することでした。基礎となる実装がコーディングした方法で機能するかどうかを理解したいのです。つまり、フレームワークのLinkedList型を使用できれば、カスタムコードを実装する意味がまったくありません。しかし、私はコンソールからの独立の良い例である改良された印刷機能が好きです。 TLDR、インタビューでフレームワークのデータ型を使用することは最初からこの煩わしさを避けてもよいですか。ありがとうございます。
追加された 著者 Peter,
@ShreeHarsha許可されているかどうかはわかりませんが、許可されていない場合は驚き、それが明確に指定されていない場合はもっと驚きます。私は既存のデータ構造を扱うコードを書くことは実証するのに有用なスキルになるだろうと思いました(そして標準ライブラリの認識を示すことは決して悪いことではありえません)。 DeleteOdd が短いほど、「より充実した」リンクリストAPIを使用してロジックをより明確にすることができることを示しています。
追加された 著者 VisualMelon,

変数の命名

  • あなたのコードを見たことがない読者を考えてみてください(おそらく、このコードを6か月以内に見直す必要がある場合には、これがあなたの立場になるでしょう)。 public MyLinkedList(int v)(vとは何ですか?)および MyLinkedList mmm = this; mmm とは何ですか。また、int変数に data という名前を付けました。データとは何ですか?

  • 私のお気に入りの MyLinkedList myll1 - 今や 'L'または 'I'が大文字になっているのか、それとも数字が '1'なのか。それを言うのは非常に難しいですし、そうすることに対して私は助言するでしょう。

  • 次に、「LinkedList」という名前には特定の意味があります。それは実際にはリンクされたリストの派生クラスですか?私はクラスの命名はかなりあいまいさがあると言っているだけで、それは明快なコードを書くことの敵です。誰かがあなたのリンクリストのインスタンスを受け取った場合、彼らは問題なく AddFirst メソッドを呼び出すことができると期待することができますか?より良い名前はこれを反映するのに適切でしょう。

  • PrintLL印刷に変更しますか? listには特定の意味があります。私はおそらく「リスト」という言葉を避けたいと思います。

(私は適切な機会を得たときに改善のためのさらなる提案を返します - そして関連するところにテストを表示します)

1
追加された

Having a quick look on your source code, it suggests that you are trying to implement a singly linked list. Is that correct? Based on that I disagree with the suggestions in the previous posts to use the LinkedList. The reason is that LinkedList implements a doubly Linked list, as one can read in the description or in the current .NET Core implementation. So if you are really looking for a single Linked List, the LinkedList will not work for you. Please dont get me wrong, if you can also use a doubly linked list then go for it and use LinkedList in the Collections, but it is important to be aware of the differences between a singly linked list and a doubly linked list.

上記のソースコードの内容に基づいて、次のリファクタリングをお勧めします。

  • Your "MyLinkedList" class is actually the "Node" in your linked List and should be named so. Please also try to use the naming conventions as they make your source code much more readable.

  • I recommend the following "deleteOdd" method:

    ListNode current = head;
    
    while (current != null)
    {
    
        if (current.data % 2 != 0)
        {
            if(current.previous != null)//As the list is non circular, if current!=head
            {
                current.previous.next = current.next;
            }
            else
            {
               setListHead(current.next);//current is your head
            }
        }
    
        current = current.next;
    }
    
    • Try to make your add functions run properly and use them for your test data. The best way to set up test preconditions is of course using Unit tests.
1
追加された
洞察力0x51baをどうもありがとうございました。これは私の質問の論理的な部分に答えます。
追加された 著者 Peter,