[LeetCode138-链表-中等] 复制带有随机指针的链表
这道题是这样的,就是说有一个链表LindedNode, 通常我们链表包含2个属性,一个是它的值val,另一个是它指向的下一个结点nextNode, 但是这个题目中的链表还有一个属性,就是它还有个随机指针,这个随机指针可能指向链表中的任意结点(包括链表的结尾null结点,或者是自己)
也就是说这个链表LindedNode的C#代码如下
public class LinkedNode { public int val; public LinkedNode next; public LinkedNode randomNode; public LinkedNode(int nodeVal = 0, LinkedNode nextNode = null, LinkedNode ranNode = null) { val = nodeVal; next = nextNode; randomNode = ranNode; } }
这道题可以用回溯算法来解决。这里,我们先来学习一下什么是回溯算法 =》 回溯是递归的一种
回溯算法,也可以称为试探法,它其实就是用的一种 “走不通就掉头”的思想 (迷途知返的策略), 它的主要思想就是在搜索尝试中找到问题的解,当不满足求解条件时就“回溯"返回,尝试别的路径
也就是说: 它从问题的某一种状态(初始状态)出发,搜索从这种状态出发所能达到的所有”状态“,当一条路走到”尽头“时(不能再前进),再后退一步或若干步,从另一种可能的”状态“出发,继续搜索,直到所有的"路径”(状态)都试探过。这种不断试探,不断前进,不断完成一条路径后,往回“回溯”再试探其他路径并最终寻找答案的方法,就是“回溯”法
这道题目用递归(回溯)方法来解决,我们需要转换正常的思路,把一个结点拆分成三个,分别是结点本身ListedNode, 结点指向的下一个结点next, 结点随机指针指向的随机结点randomNode, 我们分别复制这三个,就构成了新的链表结点 =》 这也就是说,我们每次会同时复制产生3个结点 => 那么这里问题来了,要注意重复产生的情况,比如第1次产生头结点head的复制结点copyHead, 头节点的下一个结点headNext的复制结点copyHeadNext, 头结点随机指针指向的随机结点headRandom的复制结点copyHeadRandom, 那么以后的每次复制中,我们都要特别注意这个copyHeadNext, copyHeadRandom已经存在,避免重复复制 => 举个例子,第3个结点thirdNode的随机结点也是headRandom(也就是说它的随机指向结点和第一个头结点的随机指向结点相同)=》 那么,由于copyHeadRandom已经存在,我们就不应该再次复制headRandom了
C#代码如下
自己第一次写代码时,写下了如下错误的代码:
public LinkedNode CopyLinkedNode(LinkedNode originalNode) { if (originalNode == null) return null; LinkedNode copyNode = null; while (originalNode != null) //这里会陷入无限循环,因为在这个循环内部用来递归,递归中又会调用这个循环 =》 所以这里不应该用循环 {
//这下面的写法,会存在我上面分析中copy过去的node存在重复的情况,因为很有可能原始链表中的2个node指向的随机node同一个node copyNode = new LinkedNode(originalNode.val); copyNode.next = CopyLinkedNode(originalNode.next); copyNode.randomNode = CopyLinkedNode(originalNode.randomNode); originalNode = originalNode.next; } return copyNode; }
针对上面存在的无限循环的错误情况,我们把代码改写如下:
public LinkedNode CopyLinkedNode(LinkedNode originalNode)
{
if (originalNode == null)
return null;
//这下面的写法,会存在我上面分析中copy过去的node存在重复的情况,因为很有可能原始链表中的2个node指向的随机node同一个node
ListedNode copyNode = new LinkedNode(originalNode.val);
copyNode.next = CopyLinkedNode(originalNode.next);
copyNode.randomNode = CopyLinkedNode(originalNode.randomNode);
return copyNode;
}
上面的代码,无限循环的问题不存在了,但是还会存在复制的链表中可能出现的重复的node的情况 =》 怎么解决? 解决这种重复的情况,我们通常使用哈希表来进行,C中Hashtable,Hashset,或者Dictionary(字典), 我们这里使用字典Dictionary来解决
Dictionary中键是originalNode, 值是它的复制结点copyNode, 另外,我们注意,由于里面是递归反复调用CopyLinkedNode这个方法,所以我们这个Dictionary应该放在方法CopyLinkedNode外面,否则放在方法CopyLinkedNode里面,每次递归调用时,它就重新初始化一次,以前放进去的都没了 =》改写后的代码如下
class solution { Dictionary<LinkedNode,LinkedNode> linkedDic = new Dictionary<LinkedNode,LinkedNode>(); public LinkedNode CopyLinkedNode(LinkedNode originalNode) { if (originalNode == null) return null; if(!linkedDic.ContainsKey(originalNode)) //判断当前要处理的结点是否已经在字典linkedDic中存在了
{ ListedNode copyNode = new LinkedNode(originalNode.val);
linkedDic.Add(originalNode,copyNode); //把源链表的当前结点和它的复制结点加入到字典linkedDic中 copyNode.next = CopyLinkedNode(originalNode.next); copyNode.randomNode = CopyLinkedNode(originalNode.randomNode);
} return linkedDic[originalNode]; } }
解法2: 说实话,上面这种递归回溯的解法,代码看上去简单, 但确实不容易想到。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律