【链表】剑指 Offer 35. 复杂链表的复制
题目:
请实现 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]]
解答:
方法一:
这个复制过程可以分成两步:第一步是复制原始链表上的每个节点,并用next指针相连; 第二步是设置每个节点的random指针。
第一步的实现较为简单,第二步,对于一个n个节点的链表,定位每个节点的random 指针都需要从链表头节点开始经过O(n)步才能找到,因此这种方法总的时间复杂度是O(n^2)
/* // 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 { Map<Node,Node> map = new HashMap<>(); public Node copyRandomList(Node head) { if(head == null) return null; Node res = new Node(head.val); Node pre = res; map.put(head,res); Node p = head.next; while(p!=null){ pre.next = new Node(p.val); pre = pre.next; map.put(p,pre); p = p.next; } pre.next = null; p = head; pre = res; while(p!=null){ if(p.random!=null) pre.random = map.get(p.random); else pre.random = null; p = p.next; pre = pre.next; } return res; } public Node searchNode(Node dest, Node head, Node res){ Node result = res; Node p = head; while(p!=dest){ p = p.next; result = result.next; } return result; } }
方法一的改进:
使用HashMap来简化搜索过程,使这个过程时间复杂度变为O(1)
还是分为两步:第一步仍然是将原始链表上的每个节点 N复制为N',然后把这些创建出来的节点N’连接起来。
同时我们把<N,N'>的配对信息放到一个HashMap<Node,Node> map中;
第二步设置每个节点的random指针:如果在原始链表中节点 N的random指针指向节点 S,那么在复制链表中,对应的 N'节点应该指向 S'。
/* // 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 { Map<Node,Node> map = new HashMap<>(); public Node copyRandomList(Node head) { if(head == null) return null; Node res = new Node(head.val); Node pre = res; map.put(head,res); Node p = head.next; while(p!=null){ pre.next = new Node(p.val); pre = pre.next; map.put(p,pre); p = p.next; } pre.next = null; p = head; pre = res; while(p!=null){ if(p.random!=null) pre.random = map.get(p.random); else pre.random = null; p = p.next; pre = pre.next; } return res; } public Node searchNode(Node dest, Node head, Node res){ Node result = res; Node p = head; while(p!=dest){ p = p.next; result = result.next; } return result; } }
方法二:
在不使用辅助空间的情况下实现O(n)的时问效率。
第一步:复制节点:将原始链表的任意节点 N复制为新节点N',再把N'连接到 N的后面。
即如果原始链表为A->B->C->D 则复制过后为A->A'->B->B'->C->C'->D->D'
第二步: 确定每个新增节点N'的random指针指向
显然,如果原始链表上的节点 N 的random指针指向节点S,则它对应的复制节点N'的random指针指向节点S的复制节点S',也就是当前节点S的下一个节点。
第三步: 把这个长链表拆分成两个链表,把奇数位置的节点连接起来就是原始链表,把偶数位置的节点连接起来就是复制出来的链表。
//推荐双百方法二:不需要辅助空间,但是需要额外将两个链表进行拆分,但理解难度较大 public Node copyRandomList2(Node head) { if(head==null) return null; copy2(head); randomAdd2(head); return build2(head); } public void copy2(Node head){ while(head!=null){ Node copy = new Node(head.val); copy.next = head.next; head.next =copy; head = copy.next; } } public void randomAdd2(Node head){ while(head!=null){ if(head.random!=null) head.next.random = head.random.next; head=head.next.next; } } public Node build2(Node head){ //将链表拆成两个,注意要恢复原有的链表 Node res = head.next; Node tmp = res; head.next = head.next.next;//这一步不可缺少,保证第一个复制节点对N N'的分离操作 head = head.next; while(head!=null){ tmp.next = head.next; head.next = head.next.next; tmp=tmp.next; head = head.next; } return res; }

浙公网安备 33010602011771号