【问题标题】:C# - LinkedList - How to remove all nodes after specified node?C# - LinkedList - 如何在指定节点后删除所有节点?
【发布时间】:2009-02-24 15:10:18
【问题描述】:

我正在使用通用 LinkedList 实现撤消/重做缓冲区。

在这种状态下:
[顶部]
state4(撤消)
state3(撤消)
state2 状态1
[底部]

当我进行推送时,我想删除当前状态之后的所有状态,并推送新的状态。

我目前的绕过是做while (currentState != list.last), list.removeLast();,但它很烂

LinkedList 只支持 Remove、RemoveFirst 和 removeLast...

我想要 RemoveAllNodesAfter(LinkedListNode ...) 之类的东西?

如何在不遍历所有节点的情况下很好地编写代码?也许有扩展?...

【问题讨论】:

    标签: c# linked-list


    【解决方案1】:

    我在标准LinkedList<T> 中看不到任何可以让您执行此操作的内容。如果需要,您可以查看 PowerCollectionsC5 collections - 或者只是滚动您自己的 LinkedList 类型。它是实现起来更简单的集合之一,尤其是当您可以“及时”地添加功能时。

    【讨论】:

      【解决方案2】:

      如果我自己实现这个,我会选择不同的方式来实现这个。

      我会选择创建一个.SplitAfter(node) 方法,而不是.RemoveAllNodesAfter(node) 方法,它返回一个从node 之后的下一个节点开始的新链表。这将成为一个更方便的工具,而不仅仅是能够砍掉尾巴。如果你想要你的RemoveAllNodesAfter 方法,它只需要在内部调用SplitAfter 方法并丢弃结果。

      天真的实现:

      public LinkedList<T> SplitAfter(Node node)
      {
          Node nextNode = node.Next;
      
          // break the chain
          node.Next = null;
          nextNode.Previous = null;
      
          return new LinkedList<T>(nextNode);
      }
      
      public void RemoveAllNodesAfter(Node node)
      {
          SplitAfter(node);
      }
      

      【讨论】:

      • 感谢您的回答,但 Next 和 Previous 是只读的。
      • 完全正确,我的意思是,如果您创建自己的 LinkedList 实现,我会这样做。
      【解决方案3】:

      链表(尤其是单链表)是最基本的集合结构之一。我确信您可以毫不费力地实现它(并添加您需要的行为)。

      实际上,您实际上并不需要集合类来管理列表。您可以在没有集合类的情况下管理节点。

      public class SingleLinkedListNode<T>
      {
          private readonly T value;
          private SingleLinkedListNode<T> next;
      
          public SingleLinkedListNode(T value, SingleLinkedListNode<T> next)
          {
              this.value = value;
          }
      
          public SingleLinkedListNode(T value, SingleLinkedListNode<T> next)
              : this(value)
          {
              this.next = next;
          }
      
          public SingleLinkedListNode<T> Next
          {
              get { return next; }
              set { next = value; }
          }
      
          public T Value
          {
              get { return value; }
          }
      }
      

      但是,如果您对可能的实现感兴趣,这里有一个简单的 SingleLinkedList 实现。

      public class SingleLinkedList<T>
      {
          private SingleLinkedListNode<T> head;
          private SingleLinkedListNode<T> tail;
      
          public SingleLinkedListNode<T> Head
          {
              get { return head; }
              set { head = value; }
          }
      
          public IEnumerable<SingleLinkedListNode<T>> Nodes
          {
              get
              {
                  SingleLinkedListNode<T> current = head;
                  while (current != null)
                  {
                      yield return current;
                      current = current.Next;
                  }
              }
          }
      
          public SingleLinkedListNode<T> AddToTail(T value)
          {
              if (head == null) return createNewHead(value);
      
              if (tail == null) tail = findTail();
              SingleLinkedListNode<T> newNode = new SingleLinkedListNode<T>(value, null);
              tail.Next = newNode;
              return newNode;
          }
      
          public SingleLinkedListNode<T> InsertAtHead(T value)
          {
              if (head == null) return createNewHead(value);
      
              SingleLinkedListNode<T> oldHead = Head;
              SingleLinkedListNode<T> newNode = new SingleLinkedListNode<T>(value, oldHead);
              head = newNode;
              return newNode;
          }
      
          public SingleLinkedListNode<T> InsertBefore(T value, SingleLinkedListNode<T> toInsertBefore)
          {
              if (head == null) throw new InvalidOperationException("you cannot insert on an empty list.");
              if (head == toInsertBefore) return InsertAtHead(value);
      
              SingleLinkedListNode<T> nodeBefore = findNodeBefore(toInsertBefore);
              SingleLinkedListNode<T> toInsert = new SingleLinkedListNode<T>(value, toInsertBefore);
              nodeBefore.Next = toInsert;
              return toInsert;
          }
      
          public SingleLinkedListNode<T> AppendAfter(T value, SingleLinkedListNode<T> toAppendAfter)
          {
              SingleLinkedListNode<T> newNode = new SingleLinkedListNode<T>(value, toAppendAfter.Next);
              toAppendAfter.Next = newNode;
              return newNode;
          }
      
          public void TruncateBefore(SingleLinkedListNode<T> toTruncateBefore)
          {
              if (head == toTruncateBefore)
              {
                  head = null;
                  tail = null;
                  return;
              }
      
              SingleLinkedListNode<T> nodeBefore = findNodeBefore(toTruncateBefore);
              if (nodeBefore != null) nodeBefore.Next = null;
          }
      
          public void TruncateAfter(SingleLinkedListNode<T> toTruncateAfter)
          {
              toTruncateAfter.Next = null;
          }
      
          private SingleLinkedListNode<T> createNewHead(T value)
          {
              SingleLinkedListNode<T> newNode = new SingleLinkedListNode<T>(value, null);
              head = newNode;
              tail = newNode;
              return newNode;
          }
      
          private SingleLinkedListNode<T> findTail()
          {
              if (head == null) return null;
              SingleLinkedListNode<T> current = head;
              while (current.Next != null)
              {
                  current = current.Next;
              }
              return current;
          }
      
          private SingleLinkedListNode<T> findNodeBefore(SingleLinkedListNode<T> nodeToFindNodeBefore)
          {
              SingleLinkedListNode<T> current = head;
              while (current != null)
              {
                  if (current.Next != null && current.Next == nodeToFindNodeBefore) return current;
                  current = current.Next;
              }
              return null;
          }
      }
      

      现在你可以这样做了:

      public static void Main(string[] args)
      {
          SingleLinkedList<string> list = new SingleLinkedList<string>();
          list.InsertAtHead("state4");
          list.AddToTail("state3");
          list.AddToTail("state2");
          list.AddToTail("state1");
      
          SingleLinkedListNode<string> current = null;
          foreach (SingleLinkedListNode<string> node in list.Nodes)
          {
              if (node.Value != "state2") continue;
      
              current = node;
              break;
          }
      
          if (current != null) list.TruncateAfter(current);
      }
      

      这要看你的情况,也好不到哪里去:

      public static void Main(string[] args)
      {
          SingleLinkedListNode<string> first =
              new SingleLinkedListNode<string>("state4");
          first.Next = new SingleLinkedListNode<string>("state3");
          SingleLinkedListNode<string> current = first.Next;
          current.Next = new SingleLinkedListNode<string>("state2");
          current = current.Next;
          current.Next = new SingleLinkedListNode<string>("state1");
      
          current = first;
          while (current != null)
          {
              if (current.Value != "state2") continue;
              current.Next = null;
              current = current.Next;
              break;
          }
      }
      

      这完全消除了对集合类的需要。

      【讨论】:

        【解决方案4】:

        或者,您可以这样做:

        while (currentNode.Next != null)
            list.Remove(currentNode.Next);
        

        实际上,链表是一种相当简单的数据结构,尤其是在托管代码中实现(阅读:没有内存管理麻烦)。

        这是我破解的一个,它支持足够的功能(阅读:YAGNI)来支持您的撤消/重做操作:

        public class LinkedListNode<T>
        {
            public LinkedList<T> Parent { get; set; }
            public T Value { get; set; }
            public LinkedListNode<T> Next { get; set; }
            public LinkedListNode<T> Previous { get; set; }
        }
        
        public class LinkedList<T> : IEnumerable<T>
        {
            public LinkedListNode<T> Last { get; private set; }
        
            public LinkedListNode<T> AddLast(T value)
            {
                Last = (Last == null)
                    ? new LinkedListNode<T> { Previous = null }
                    : Last.Next = new LinkedListNode<T> { Previous = Last };
        
                Last.Parent = this;
                Last.Value = value;
                Last.Next = null;
        
                return Last;
            }
        
            public void SevereAt(LinkedListNode<T> node)
            {
                if (node.Parent != this)
                    throw new ArgumentException("Can't severe node that isn't from the same parent list.");
        
                node.Next.Previous = null;
                node.Next = null;
                Last = node;
            }
        
            IEnumerator IEnumerable.GetEnumerator()
            {
                return ((IEnumerable<T>)this).GetEnumerator();
            }
        
            public IEnumerator<T> GetEnumerator()
            {
                var walk = Last;
        
                while (walk != null) {
                    yield return walk.Value;
                    walk = walk.Previous;
                }
            }
        
        }
        

        然后你可以在你的代码中使用SevereAt 方法来简单的“剪切”链表。

        【讨论】:

          【解决方案5】:

          首先想到的是设置Node.Next.Previous = null(如果它是一个双向链表),然后设置Node.Next = null

          不幸的是,因为LinkedListNode&lt;T&gt;.NextLinkedListNode&lt;T&gt;.Previous 在链接列表的.NET 实现中是只读属性,我认为您可能必须实现自己的结构才能实现此功能。

          但正如其他人所说,这应该很容易。如果您只是 Google 搜索 C# 链接列表,则可以使用大量资源作为起点。

          【讨论】:

            【解决方案6】:
            if(this.ptr != null && this.ObjectName != null)
            {
                LinkedListNode<ObjectType> it = ObjectName.Last;
                            for (; it != this.ptr; it = it.Previous) 
                            {
                                this.m_ObjectName.Remove(it);
                            }
            }
            

            this.ptrLinkedListNode&lt;ObjectType&gt; 类型,仅供参考

            this.ptr 是指向您当前所在节点的指针,我假设您要删除它右侧的所有内容。

            不要复制你的结构,这是最糟糕的主意。它是一个完整的内存猪,结构可能非常大。除非绝对必要,否则复制对象不是好的编程习惯。尝试进行就地操作。

            【讨论】:

              【解决方案7】:

              我做了两种扩展方法,分别是“删除特定节点之前的所有节点”和“删除特定节点之后的所有节点”。但是,这些扩展方法是 LinkedListNode 的扩展,而不是 LinkedList 本身,只是为了方便:

              public static class Extensions
              {
                  public static void RemoveAllBefore<T>(this LinkedListNode<T> node)
                  {
                      while (node.Previous != null) node.List.Remove(node.Previous);
                  }
              
                  public static void RemoveAllAfter<T>(this LinkedListNode<T> node)
                  {
                      while (node.Next != null) node.List.Remove(node.Previous);
                  }
              }
              

              使用示例:

              void Main()
              {
                  //create linked list and fill it up with some values
              
                  LinkedList<int> list = new LinkedList<int>();
                  for(int i=0;i<10;i++) list.AddLast(i);
              
                  //pick some node from the list (here it is node with value 3)
              
                  LinkedListNode<int> node = list.First.Next.Next.Next;
              
                  //now for the trick
              
                  node.RemoveAllBefore();
              
                  //or
              
                  node.RemoveAllAfter();
              }
              

              好吧,这不是最有效的方法,如果您发现自己在大型列表上或经常调用此方法,那么此处描述的其他方法可能更合适(例如编写您自己的链表类,它允许按照其他答案中的描述进行拆分) 但如果只是偶尔的“从这里和那里删除节点”,那么这很简单且非常直观。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2013-11-02
                • 2015-08-26
                • 2019-01-13
                • 1970-01-01
                • 1970-01-01
                • 2013-11-24
                • 1970-01-01
                相关资源
                最近更新 更多