题目要求:
一个链表除了带有next指针指向下一个元素之外,还有另外一个指针rand,该指针随机指向链表中的任意一个节点,现在要求对这个链表进行深度的拷贝,返回拷贝了之后的链表的头结点。
思路分析与代码实现:
package com.isea.brush;
import java.util.HashMap;
/**
* 实现对一个带有随机节点的链表的拷贝
* 准备一个辅助的映射map,第一次遍历链表,将当前链表的节点和当前节点的拷贝,分别作为key和value存放在map中
* 第二次遍历链表,将拷贝的节点,按照原来链表的次序连接起来,
*/
public class CopyListWithRandom {
private static class Node {
private Integer data;
private Node next;
private Node rand;
public Node(int data) {
this.data = data;
}
public static Node copyListWithRand1(Node head) {
HashMap<Node, Node> map = new HashMap<Node, Node>();
Node cur = head;
while (cur != null) {
map.put(cur, new Node(cur.data));
cur = cur.next;
}
cur = head;
while (cur != null) {
map.get(cur).next = map.get(cur.next);
map.get(cur).rand = map.get(cur.rand);
cur = cur.next;
}
return map.get(head); // map.get(null) 不会报错,会直接放回一个null
}
public static void printRandLinkedList(Node head) {
Node cur = head;
System.out.print("order:");
while (cur != null) {
System.out.print(cur.data + " ");
cur = cur.next;
}
cur = head;
System.out.println();
System.out.print("random:");
while (cur != null) {
System.out.print(cur.rand == null ? "- " : cur.rand.data + " ");
cur = cur.next;
}
System.out.println();
}
public static void main(String[] args) {
Node head = null;
Node res = null;
printRandLinkedList(head);
res = copyListWithRand1(head);
printRandLinkedList(res);
System.out.println("=========================");
head = new Node(1);
head.next = new Node(2);
head.next.next = new Node(3);
head.next.next.next = new Node(4);
head.next.next.next.next = new Node(5);
head.next.next.next.next.next = new Node(6);
head.rand = head.next.next.next.next.next; // 1 -> 6
head.next.rand = head.next.next.next.next.next; // 2 -> 6
head.next.next.rand = head.next.next.next.next; // 3 -> 5
head.next.next.next.rand = head.next.next; // 4 -> 3
head.next.next.next.next.rand = null; // 5 -> null
head.next.next.next.next.next.rand = head.next.next.next; // 6 -> 4
printRandLinkedList(head);
res = copyListWithRand1(head);
printRandLinkedList(res);
printRandLinkedList(head);
System.out.println("=========================");
}
}
/**
* order:
* random:
* order:
* random:
* =========================
* order:1 2 3 4 5 6
* random:6 6 5 3 - 4
* order:1 2 3 4 5 6
* random:6 6 5 3 - 4
* order:1 2 3 4 5 6
* random:6 6 5 3 - 4
* =========================
*/
}
上面的这种算法的时间复杂度是O(N),因为这里的哈希表的增删改查的时间复杂度都是O(1)的,这里的额外空间复杂度是O(N)
的。这里我们进一步提出要求:
如果要时间复杂度是O(N)额外空间复杂度是O(1)的,应该如何去解决?
代码实现:
package com.isea.brush;
/**
* 实现对一个带有随机节点的链表的拷贝,要求时间复杂度是O(N),额外空间复杂度是O(1)的
* 实现思路:遍历一下链表,拷贝当前的节点(cur)为copy,让当前节点的next指针指向copy,遍历完整个数组之后,形成一个大链表
* 再一次遍历链表,一次移动两个节点,并接收住,one ,two,根据one找到one的rand节点,该节点的下一个节点,就是two的rand指针
* 应该指向的节点,遍历完成之后,在将整个链表分离成为两个节点。
*/
public class CopyListWithRandom {
private static class Node {
private Integer data;
private Node next;
private Node rand;
public Node(int data) {
this.data = data;
}
public static Node copyListWithRand1(Node head) {
if (head == null){
return null;
}
Node cur = head;
Node next = null;
// 拷贝数组,形成一个大链表
while(cur != null){
next = cur.next;
cur.next = new Node(cur.data);
cur.next.next = next;
cur = next;
}
// copy the rand
cur = head;
Node curCopy = null;
while(cur != null){
next = cur.next.next;
curCopy = cur.next;
curCopy.rand = cur.rand != null ? cur.rand.next : null;
cur = next;
}
Node res = head.next;
cur = head;
// split the list
while(cur != null){
next = cur.next.next;
curCopy = cur.next;
cur.next = next;
curCopy.next = next != null ? next.next : null;
cur = next;
}
return res;
}
public static void printRandLinkedList(Node head) {
Node cur = head;
System.out.print("order:");
while (cur != null) {
System.out.print(cur.data + " ");
cur = cur.next;
}
cur = head;
System.out.println();
System.out.print("random:");
while (cur != null) {
System.out.print(cur.rand == null ? "- " : cur.rand.data + " ");
cur = cur.next;
}
System.out.println();
}
public static void main(String[] args) {
Node head = null;
Node res = null;
printRandLinkedList(head);
res = copyListWithRand1(head);
printRandLinkedList(res);
System.out.println("=========================");
head = new Node(1);
head.next = new Node(2);
head.next.next = new Node(3);
head.next.next.next = new Node(4);
head.next.next.next.next = new Node(5);
head.next.next.next.next.next = new Node(6);
head.rand = head.next.next.next.next.next; // 1 -> 6
head.next.rand = head.next.next.next.next.next; // 2 -> 6
head.next.next.rand = head.next.next.next.next; // 3 -> 5
head.next.next.next.rand = head.next.next; // 4 -> 3
head.next.next.next.next.rand = null; // 5 -> null
head.next.next.next.next.next.rand = head.next.next.next; // 6 -> 4
printRandLinkedList(head);
res = copyListWithRand1(head);
printRandLinkedList(res);
printRandLinkedList(head);
System.out.println("=========================");
}
}
/**
* order:
* random:
* order:
* random:
* =========================
* order:1 2 3 4 5 6
* random:6 6 5 3 - 4
* order:1 2 3 4 5 6
* random:6 6 5 3 - 4
* order:1 2 3 4 5 6
* random:6 6 5 3 - 4
* =========================
*/
}