我正在实现一个带有通用LinkedList的撤销/重做缓冲区。
在这种状态下:
[置顶]
state4(undone)
state3(撤消)
state2< - 当前状态
状态1
[底部]
当我执行Push时,我想删除当前状态之后的所有状态,并推送新状态。
我目前的旁路是while (currentState != list.last), list.removeLast();
,但很糟糕
LinkedList只支持Remove,RemoveFirst& removeLast ...
我想要像RemoveAllNodesAfter(LinkedListNode ...)?
这样的东西如何在不迭代所有节点的情况下很好地编码?也许有扩展名?...
答案 0 :(得分:6)
我无法在标准LinkedList<T>
中看到任何可以让您执行此操作的内容。如果需要,您可以查看PowerCollections和C5 collections,或者只是滚动自己的LinkedList
类型。它是要实现的更简单的集合之一,特别是如果您能够以“及时”的方式添加功能。
答案 1 :(得分: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);
}
答案 2 :(得分:4)
链接列表(尤其是单链表)是最基本的基本集合结构之一。我确信你可以毫不费力地实现它(并添加你需要的行为)。
实际上,您实际上并不需要集合类来管理列表。您可以在没有集合类的情况下管理节点。
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;
}
}
这完全消除了对集合类的需要。
答案 3 :(得分: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
方法“剪切”链接列表,简单明了。
答案 4 :(得分:0)
首先想到的是设置Node.Next.Previous = null
(如果它是双向链表),然后设置Node.Next = null
。
不幸的是,因为LinkedListNode<T>.Next
和LinkedListNode<T>.Previous
是链接列表的.NET实现中的只读属性,我认为您可能必须实现自己的结构才能实现此功能。
但正如其他人所说,这应该很容易。如果您只是谷歌的链接列表C#,那么您可以使用大量资源作为起点。
答案 5 :(得分: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
是指向您当前所在节点的指针,我假设您要删除其右侧的所有内容。
不要制作你的结构的新副本,这是有史以来最糟糕的想法。这是一个完整的记忆猪,结构可能非常大。除非绝对必要,否则复制Object并不是一种好的编程习惯。尝试进行就地操作。
答案 6 :(得分: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();
}
这不是最有效的方法,如果你发现自己在大型列表上或经常调用此方法,那么此处描述的其他方法可能更合适(比如编写自己的链表类,允许按其他答案中的描述进行拆分) )但如果只是偶尔“在这里和那里删除节点”,这比这简单而且非常直观。