You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

复制C# LinkedList时同步LinkedListNode指针?求O(n)以下方案

问题分析

现有方法接收LinkedList<T>和LinkedListNode<T>参数,以current节点作为锚点执行操作,但复制链表后直接使用原节点在复制链表上操作会抛出异常:

System.InvalidOperationException: 'The LinkedList node does not belong to current LinkedList.'

已知通过copy.Find(current.Value)能找到复制链表中对应的节点,但该方法时间复杂度为O(n),希望找到更低复杂度的实现方案。实际场景为:需对带current指针的链表执行破坏性操作,后续可能需要回滚状态(包括current指针的位置),当前考虑通过记录操作并反向执行来实现回滚。

解决方案

1. 复制链表时同步建立节点映射(O(n)复制时间,后续操作O(1))

复制链表的同时,用字典记录原节点到复制节点的映射关系,后续可直接通过原current节点快速获取复制链表中的对应节点,避免重复遍历:

public static void Foo<T>(LinkedList<T> list, LinkedListNode<T> current)
{
    var nodeMap = new Dictionary<LinkedListNode<T>, LinkedListNode<T>>();
    var copy = new LinkedList<T>();
    
    // 遍历原链表,复制节点并建立映射
    var originalNode = list.First;
    while (originalNode != null)
    {
        var copiedNode = copy.AddLast(originalNode.Value);
        nodeMap.Add(originalNode, copiedNode);
        originalNode = originalNode.Next;
    }
    
    // 直接通过映射获取复制链表中的对应节点
    var copyCurrent = nodeMap[current];
    copy.Remove(copyCurrent);
    // 其他操作如copy.AddAfter(copyCurrent, ...)可正常执行
}

该方案复制链表的时间复杂度仍为O(n),但后续查找对应节点为O(1),远优于多次调用Find的重复O(n)遍历,尤其适合需要多次操作锚点节点的场景。

2. 基于操作日志的回滚方案(避免复制整个链表)

既然核心需求是操作回滚,完全可以跳过链表复制环节,改为记录每次破坏性操作的细节,回滚时反向执行操作即可:

  • 记录操作类型(删除、插入前、插入后等)
  • 记录操作涉及的节点上下文(如被删节点的前后节点、插入位置的锚点节点等)
  • 记录操作前后current指针的位置

示例实现思路:

// 定义操作日志类,存储回滚所需信息
public class LinkedListOperationLog<T>
{
    public enum OperationType { Remove, AddAfter, AddBefore }
    public OperationType Type { get; set; }
    public T NodeValue { get; set; }
    public LinkedListNode<T> AnchorNode { get; set; }
    public LinkedListNode<T> OldPrev { get; set; }
    public LinkedListNode<T> OldNext { get; set; }
    public LinkedListNode<T> OldCurrent { get; set; }
}

// 执行操作时记录日志
var operationLogs = new List<LinkedListOperationLog<int>>();
var list = new LinkedList<int>() { 1, 2, 3, 4, 5 };
var current = list.First;

// 模拟删除操作并记录日志
var nodeToRemove = current;
var removeLog = new LinkedListOperationLog<int>
{
    Type = LinkedListOperationLog<int>.OperationType.Remove,
    NodeValue = nodeToRemove.Value,
    OldPrev = nodeToRemove.Previous,
    OldNext = nodeToRemove.Next,
    OldCurrent = current
};
list.Remove(nodeToRemove);
operationLogs.Add(removeLog);

// 回滚操作:反向执行最后一次操作
var lastLog = operationLogs.Last();
if (lastLog.Type == LinkedListOperationLog<int>.OperationType.Remove)
{
    LinkedListNode<int> restoredNode;
    if (lastLog.OldPrev == null)
        restoredNode = list.AddFirst(lastLog.NodeValue);
    else if (lastLog.OldNext == null)
        restoredNode = list.AddLast(lastLog.NodeValue);
    else
        restoredNode = list.AddAfter(lastLog.OldPrev, lastLog.NodeValue);
    // 恢复current指针位置
    current = lastLog.OldCurrent;
}

该方案无需复制整个链表,空间复杂度取决于操作次数,回滚单步操作的时间复杂度为O(1),在操作次数远少于链表节点数的场景下,效率远高于复制链表的方案。

关键说明

LinkedListNode<T>内部持有对所属LinkedList<T>实例的引用,复制链表仅会复制节点的值,原节点与复制节点分属不同链表实例,因此直接使用原节点在复制链表上操作必然触发异常,必须获取复制链表内对应的节点实例。


内容的提问来源于stack exchange,提问作者CWKSC

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 03:16:25