Reputation: 2773
I am implementing an undo/redo buffer with generic LinkedList.
In this state:
[Top]
state4 (undone)
state3 (undone)
state2 <-- current state
state1
[bottom]
When I do a Push, I would like to remove all states after the current one, and push the new one.
My current bypass is to do while (currentState != list.last), list.removeLast();
but it sucks
LinkedList just support Remove, RemoveFirst & removeLast...
I would like something like RemoveAllNodesAfter(LinkedListNode ...) ?
How can I code that nicely, without iterating throught all nodes ? Maybe with extensions ?...
Upvotes: 6
Views: 12118
Reputation: 101
In my game program, I'm keeping track of all the board positions of all the turns in a linked list. To insert a new position in the middle of the list, and remove all the nodes after it, I used this code...
// ################
//
// ADD HISTORY NODE
//
// ################
//
// IF REPLAY NODE NOT LAST NODE
//
// REMOVE ALL NODES AFTER REPLAY NODE
//
// ADD HISTORY NODE TO END
//
public void addHistoryNode()
{
while (replay_node != positions.Last)
{
positions.RemoveLast();
}
positions.AddLast(getLivePosition());
replay_node = positions.Last;
}
Upvotes: 0
Reputation: 11
I've made two extension methods for "removing all nodes before specific node" and "removing all nodes after specific node". However, these extension methods are extensions of LinkedListNode, not LinkedList itself, just for convenience:
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);
}
}
Example of use:
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();
}
Well it's not the most effective approach and if you find yourself calling this method on large lists or very often, then other here described approaches are probably more fit (like writing your own linked list class which allows splitting as described in other answers) but if it is just occassional "remove node here and there" than this is simple and quite intuitive.
Upvotes: 0
Reputation:
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
is of type LinkedListNode<ObjectType>
just fyi
this.ptr
is a pointer pointing to the node you are currently at, I'm assuming you want to delete everything to the right of it.
Don't make a new copy of your structure, its the worst idea ever. It is a complete memory hog and the structure could be extremely large. Copying the Object is not good programming practice unless its absolutely necessary. Try to do in-place operations.
Upvotes: 0
Reputation: 5232
The first idea that springs to mind is to set Node.Next.Previous = null
(if it's a doubly-linked list) and then Node.Next = null
.
Unfortunately, because LinkedListNode<T>.Next
and LinkedListNode<T>.Previous
are read-only properties in the .NET implementation of Linked List, I think you may have to implement your own structure to achieve this functionality.
But as others have said, that should be easy enough. There are plenty of resources you can use as a starting point if you just Google for linked lists C#.
Upvotes: 0
Reputation: 61518
Alternatively, you can do this:
while (currentNode.Next != null)
list.Remove(currentNode.Next);
Actually, a linked list is a fairly trivial data structure to implement especially in managed code (read: no memory management hassle).
Here's one I hacked up that support just enough functions (read: YAGNI) to support your undo/redo operations:
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;
}
}
}
Then you can use the SevereAt
method in your code to "cut" the linked list nice and simple.
Upvotes: 3
Reputation: 28416
Linked List (especially the singly linked list) is one of the most basic fundamental collection structures. I'm certain that you could probably implement it (and add the behavior your need) with little effort.
In reality, you don't actually need a collection class to manage the list. You could manage the nodes without a collection class.
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; }
}
}
If you're interested in a possible implementation, however, here's a somewhat simple SingleLinkedList implementation.
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;
}
}
Now you can do this:
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);
}
The thing is depending on your situation, it's not much better than this:
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;
}
}
This eliminates the need for the collection class altogether.
Upvotes: 5
Reputation: 391396
If I were to implement this myself, I would chose a different way to implement this.
Instead of the .RemoveAllNodesAfter(node)
method, I would opt to make a .SplitAfter(node)
method that returned a new linked list starting with the next node after node
. This would make a handier tool than just being able to chop off the tail. If you wanted your RemoveAllNodesAfter
method, it would just have to call the SplitAfter
method internally and discard the result.
Naive implementation:
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);
}
Upvotes: 6
Reputation: 1500855
I can't see anything in the standard LinkedList<T>
which lets you do this. You could look in PowerCollections and the C5 collections if you want - or just roll your own LinkedList
type. It's one of the simpler collections to implement, especially if you can add functionality in a "just in time" manner.
Upvotes: 7