2009-02-24 4 views
3

Я реализую буфер отмены/повтора с общим LinkedList.C# - LinkedList - Как удалить все узлы после указанного узла?

В этом состоянии:
[Вверх]
state4 (отменяются)
state3 (отменяются)
State2 < - текущее состояние
State1
[снизу]

Когда я сделать Push, я хотел бы удалить все состояния после текущего и нажать новый.

Мой текущий байпас сделать while (currentState != list.last), list.removeLast(); но это отстой

LinkedList просто поддержка Удалить, RemoveFirst & removeLast ...

Я хотел бы что-то вроде RemoveAllNodesAfter (LinkedListNode ...)?

Как я могу правильно это кодировать, без повторения всех узлов? Может быть, с расширениями? ...

ответ

5

Я ничего не вижу в стандарте LinkedList<T>, который позволяет вам это делать. Вы можете посмотреть в PowerCollections и C5 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

+0

Это совершенно правильно, я имел в виду, что если вы создадите собственную реализацию LinkedList, я бы так и сделал. –

0

Первая мысль, которая приходит на ум, чтобы установить Node.Next.Previous = null (если это дважды связанный список), а затем Node.Next = null.

К сожалению, поскольку LinkedListNode<T>.Next и LinkedListNode<T>.Previous являются свойствами, доступными только для чтения, в .NET-реализации Связанного списка, я думаю, вам, возможно, придется реализовать свою собственную структуру для достижения этой функциональности.

Но, как говорили другие, это должно быть достаточно легко. Есть много ресурсов, которые вы можете использовать в качестве отправной точки, если вы просто используете Google для связанных списков C#.

2

В качестве альтернативы, вы можете сделать это:

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

На самом деле, связанный список является довольно тривиальной структурой данных для реализации, особенно в управляемом коде (читать: нет управления хлопот памяти).

Вот один я изрубил, что поддержка только достаточно функций (читай: YAGNI) для поддержки Undo/Redo операции:

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.ptr имеет тип LinkedListNode<ObjectType> просто FYI

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(); 
} 

Ну это не самый эффективный подход, и если вы окажетесь вызова этого метод в больших списках или очень часто, то другие описанные здесь подходы, вероятно, более подходят (например, для написания собственного связанного класса списка, который позволяет расщепить, как описано в других ответах), но если это просто случайный «удалить узел здесь и там», простой и довольно интуитивный.

Смежные вопросы