[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:  说实话,上面这种递归回溯的解法,代码看上去简单, 但确实不容易想到。

posted on   新西兰程序员  阅读(4)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示