所谓带随机指针的链表,结构如下:
class Node { int val; Node next; Node random; public Node(int val) { this.val = val; this.next = null; this.random = null; } }
除next外,还有一个随机指针random,随机指向链表中的某个元素(当然 :random也可能为null). 复制的难度在于, 新节点刚new出来时,其random指向的另外1个“新”节点,可能还没复制出来(即:首次无法确定新节点的random该指向谁,除非所有老节点全复制完)
有二种做法:
1、借助额外的Map记录“新-老”节点的映射
public Node copyRandomList(Node head) { if (head==null){ return null; } Node h = head; Map<Node,Node> map = new HashMap<>(); Node newHead = new Node(head.val); Node curr = newHead; //第一轮,复制节点,random挂空,同时记录处理过的老节点与新节点的映射关系 while(head!=null){ if (head.next!=null){ Node newNext = new Node(head.next.val); curr.next = newNext; } map.put(head,curr); head = head.next; curr = curr.next; } head = h; //第二轮,处理random指向,通过map映射,查到random指向的新节点 while(head!=null){ Node newNode = map.get(head); if (head.random!=null){ newNode.random = map.get(head.random); } head = head.next; } return newHead; }
2、如果要求空间复杂度为O(1),还有1个比较巧妙的思路
a、 先把每个节点复制一份, 挂在自己身后,相当于A -> B -> C 变成 A -> A' -> B -> B' -> C -> C'
b、 每2轮,处理random时,通过身后的“影子”,就能找到random的新节点在哪
c、 将链表分离, A -> A' -> B -> B' -> C -> C' 变成 A -> B -> C 和A' -> B' -> C' 返回A'
public Node copyRandomList(Node head) { if (head==null){ return head; } Node curr = head; //复制自身,挂在身后 while(curr!=null){ Node newNode = new Node(curr.val); newNode.next = curr.next; curr.next = newNode; curr = curr.next.next; } //复制random curr = head; while(curr!=null){ if (curr.random!=null){ curr.next.random = curr.random.next; } curr = curr.next.next; } //分离链表 curr = head; Node newHead = head.next; while(curr!=null){ Node newCurr = curr.next; curr.next = curr.next.next; if (newCurr.next!=null){ newCurr.next = newCurr.next.next; } curr = curr.next; } return newHead; }