// 【问题】复制含有随机指针节点的链表
// 【问题描述】有一种特殊的链表节点类描述如下:
//public class Node{
// public int value;
// public Node next;
// public Node rand;
// public Node(int data) {
// this.value = data;
// }
//}
// Node 类中value是节点值,next指针和正常的单链表中的next指针意义一样,都指向下一个节点,rand指针是Node类中
// 新增的指针,它可能指向链表中的任意一个节点,也可能指向null。
//【要求】给定一个有Node节点类型组成的无环单链表的头节点head,实现一个函数完成这个单链表的复制,并返回复制的新链表的头节点。
//【进阶要求】不使用额外数据结构,只使用有限的几个变量,且在时间复杂度为O(N)
【求解思路】
【方法一】借助哈希表,额外空间
【方法二】直接在原来的链表插入
//求解思路:用Hashmap的思路来做,使用了哈希表,使用了额外空间,进行拷贝:
//0)定义这个特殊的链表结构
//1)第一次遍历原始链表 -> value:用HashMap结构复制节点值:Key为Node节点1,Value为拷贝的Node
//2)第二次遍历原始链表:-> next指针 + rand指针复制:
// 因为1的next指针能够找到2, 2通过哈希表可以找到2’, 所以1’的next指针连接2’,
// 因为1的next指针能够找到3, 3可以通过哈希表找到3’,所以让1’的rand指针连接3’
// 遍历2的过程重复以上操作
// 最终返回1’
【代码】
public class Copy_List_With_Random {
public static class Node{
private int value;
private Node next;
private Node rand;
public Node(int data) {
this.value = data;
}
}
//【方法一: 使用额外空间HashMap表】
//自己定义的函数,最后返回一个Node节点,后面是定义的名字及需要传入的参数,O(N),装进去N个数
public static Node copyListWithRand1(Node head) {
HashMap<Node, Node> map = new HashMap<>(); // 建立一个哈希表
Node cur = head; // 建立cur,指针指向链表的头
// 第一次遍历链表,map,原来的节点与复制节点
while (cur != null) {
map.put(cur, new Node(cur.value)); // 建立哈希表,里面放入2个节点Node
cur = cur.next;
}
// 第二次遍历链表, 设置拷贝后的节点的next指针与rand指针
cur = head;
while(cur != null) {
map.get(cur).next = map.get(cur.next);// 1节点对应的哈希表值为1’节点 的next指针 = (2’节点)1号节点的next为2号,2号的哈希表值为2’节点
map.get(cur).rand = map.get(cur.rand);
cur = cur.next;
}
return map.get(head);
}
//【方法二:直接把复制的节点放在原来的后面(插入),修改指针指向, 不使用额外空间,仅仅使用几个变量】
public static Node copyListWithRand2(Node head) {
if (head == null) {
return null;
}
Node cur = head;
Node next = null;
//把复制的Node插入到原来的链表中,省去了哈希表的空间
while(cur != null) {
next = cur.next; // 原来第二个节点给next
cur.next = new Node(cur.value);
cur.next.next = next;
cur = next;
}
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
while (cur != null) {
next = cur.next.next;
curcopy = cur.next;
cur.next = next;
curcopy.next = next != null ? next.next : null;
}
return res;
}
public static void printRandLinkedList(Node head) {
Node cur = head;
System.out.print("order: ");
while (cur != null) {
System.out.print(cur.value + " ");
cur = cur.next;
}
System.out.println();
cur = head;
System.out.print("rand: ");
while (cur != null) {
System.out.print(cur.rand == null ? "- " : cur.rand.value + " ");
cur = cur.next;
}
System.out.println();
}
public static void main(String[] args) {
Node head = null;
Node res1 = null;
Node res2 = null;
printRandLinkedList(head);
res1 = copyListWithRand1(head);
printRandLinkedList(res1);
res2 = copyListWithRand2(head);
printRandLinkedList(res2);
printRandLinkedList(head);
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);
res1 = copyListWithRand1(head);
printRandLinkedList(res1);
res2 = copyListWithRand2(head);
printRandLinkedList(res2);
printRandLinkedList(head);
System.out.println("=========================");
}
}
【运行结果】
order:
rand:
order:
rand:
order:
rand:
order:
rand:
=========================
order: 1 2 3 4 5 6
rand: 6 6 5 3 - 4
order: 1 2 3 4 5 6
rand: 6 6 5 3 - 4
Exception in thread "main" java.lang.NullPointerException
at Copy_List_With_Random$Node.access$1(Copy_List_With_Random.java:32)
at Copy_List_With_Random.copyListWithRand2(Copy_List_With_Random.java:86)
at Copy_List_With_Random.main(Copy_List_With_Random.java:144)