2009-02-24 85 views
3

我正在实现一个具有通用LinkedList的撤销/重做缓冲区。C# - LinkedList - 如何删除指定节点后的所有节点?

在该状态下:
[顶端]
state4(撤消)
STATE3(撤消)
STATE2 < - 当前状态
STATE1
[底部]

当我做一个推,我想删除当前之后的所有状态,并推新的状态。

我的电流旁路是做while (currentState != list.last), list.removeLast();但它吮吸

的LinkedList只是支持删除,RemoveFirst & removeLast ...

我想是这样RemoveAllNodesAfter(一个LinkedListNode ...)?

我怎样才能很好地编写代码,而无需迭代所有节点?也许有扩展名?...

回答

5

我看不到任何东西在标准LinkedList<T>,它可以让你这样做。如果需要,您可以查看PowerCollectionsC5 collections - 或者只是推出自己的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; 
    } 
} 

这消除了对集合类的需求共。

5

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

而不是.RemoveAllNodesAfter(node)方法,我会选择使.SplitAfter(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); 
} 
+0

感谢您的回答,但Next和Previous是只读的。 – rockeye 2009-02-24 15:54:43

0

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

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

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

2

或者,你可以这样做:

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方法在代码中“切割”链接列表很好,很简单。

0
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<ObjectType>类型仅供参考

this.ptr是一个指针指向你是在目前,我假设你要删除一切,它的右侧的节点。

不要制作你的结构的新副本,它是有史以来最糟糕的想法。这是一个完整的记忆猪,结构可能非常大。除非绝对必要,否则复制对象不是很好的编程习惯。尝试做就地操作。

0

我做了两个扩展方法,用于“在特定节点之前移除所有节点”和“在特定节点之后移除所有节点”。然而,这些扩展方法是一个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(); 
} 

那么它是不是最有效的方法,如果你发现自己在调用此方法在大列表上或经常,然后其他这里描述的方法可能更适合(如编写自己的链接列表类允许分裂,如其他答案中所述),但如果它只是偶尔“删除节点在这里和那里”比这是简单而直观。