【剑指Offer1】复杂链表的复制
作者:互联网
题目:
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
例如
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
思路:
一开始没想通题目啥意思,后面明白了其实是自己引用类型学的不好。定义一个新的node用原有链表进行赋值,只是给原有链表的节点增加了一个引用,并不是复制了该节点。
真正的复制,应该是new一个全新的空node,用对应原链表的val进行赋值,当然next和random也必须指向自己创建的节点。
到这里可以明确链表至少需要遍历两遍。因为首次创建新链表节点时,random指向的节点可能没有创建,必须要将新链表的节点全部创建好串起来,才可以再遍历更新random的指向。
那么问题就在于,怎么把旧链表的节点关系,在新链表两个全新的节点中重现?
A代表新链表节点,B代表旧链表节点,即需要找到A.random和B.random的对应关系,进一步想到,只需要拥有A表每个节点与之对应的B表节点的关系映射,问题就可以解决,故想到HashMap。
在遍历创建B表的同时,以A表节点作为key,B表节点作为value,将AB的对应关系存在map中。第二次遍历更新random指针时,只要将A对应的B取出,之后将A.random对应的B节点赋值给B.random即可
代码如下:
/* // Definition for a Node. class Node { int val; Node next; Node random; public Node(int val) { this.val = val; this.next = null; this.random = null; } } */ class Solution { public Node copyRandomList(Node head) { // 处理边界值 if (head == null) { return null; } Map<Node, Node> nmap = new HashMap<>(); Node newHead = new Node(head.val); Node curNode = head; nmap.put(head, newHead); // 生成基础链表与映射map while (curNode.next != null) { // 生成新节点 Node newNode = new Node(curNode.next.val); // 取出上一个节点 Node preNode = nmap.get(curNode); // 将新节点与上一个节点相连; preNode.next = newNode; // 在map中更新上一个节点,存入新节点 nmap.put(curNode.next, newNode); curNode = curNode.next; } // 更新random指针 curNode = head; while (curNode != null) { // 查询random节点 Node randomNode = curNode.random; if (randomNode != null) { nmap.get(curNode).random = nmap.get(randomNode); } else { nmap.get(curNode).random = null; } curNode = curNode.next; } return newHead; } }
总结:
当然还是有更牛的大佬,用了更少的空间,采用了拼接/拆分链表的方法解答,代码如下:
/* // Definition for a Node. class Node { int val; Node next; Node random; public Node(int val) { this.val = val; this.next = null; this.random = null; } } */ //拼接+拆分 /** 1 复制各节点,构建拼接链表: 设原链表为node1→node2→⋯ ,构建的拼接链表如下所示: node1→node1 new→node2→node2 new→⋯ 2 构建新链表各节点的 random 指向: 当访问原节点 cur 的随机指向节点 cur.random 时,对应新节点 cur.next 的随机指向节点为 cur.random.next 。 3 拆分原 / 新链表: 设置 pre / cur 分别指向原 / 新链表头节点,遍历执行 pre.next = pre.next.next 和 cur.next = cur.next.next 将两链表拆分开。 返回新链表的头节点 res 即可。 */ class Solution { public Node copyRandomList(Node head) { if(head==null) return null; Node cur =head; //1 复制各节点,构建拼接链表 while(cur!=null){ Node tmp=new Node(cur.val); tmp.next=cur.next; cur.next=tmp; cur=tmp.next; } //2 构建新链表各节点的 random 指向 cur=head; while(cur!=null){ if(cur.random!=null){ cur.next.random=cur.random.next; } cur=cur.next.next; } //3 拆分原 / 新链表 cur=head.next; Node pre=head,res=head.next; while(cur.next!=null){ pre.next=pre.next.next; cur.next=cur.next.next; pre=pre.next; cur=cur.next; } pre.next=null;//单独处理原链表尾结点 return res; } }
标准答案,学习一个。
就是通过“”自己的下一个是自己的复制“”,做到了映射关系的管理,很强!
标签:Node,cur,random,next,链表,Offer1,复制,节点 来源: https://www.cnblogs.com/ProbablyTrue/p/15780118.html