1、复制含有随机指针节点的链表
1. 1 简单描述
就是单链表的每一个节点不只有next,还多了个rand,指向其他的节点或者指向null
1. 2 思路
有两种方法,
- 第一种是利用外部辅助–hashMap,你来一个我存一个,并且是键值对,逐个对应,然后输出的时候再建立复制链表的节点之间的关系,空间复杂度为O(N)
- 第二种就是在源链表的每个节点后面插入我们要复制的节点,1 > 1_ > 2 > 2_ > 3 > 3_,其中带下标的是我们复制的链表节点,建立节点之间的关系,最后split,分割变成两个链表,空间复杂度为O(1)
具体请看如下代码:
package cn.nupt;
import java.util.HashMap;
/**
* @Description: 复制含有随机指针节点的链表,返回复制节点的头节点
*
* @author PizAn
* @Email pizan@foxmail.com
* @date 2019年2月19日 下午8:25:07
*
*/
public class CopyListWithRandom {
// 有两种方法,第一种是利用外部辅助--hashMap,你来一个我存一个,并且是键值对,一一对应,然后输出的时候再建立复制链表的节点之间的关系
// 第二种就是O(1),1 > 1_ > 2 > 2_ > 3 > 3_,
// 其中带下标的是我们复制的链表节点,建立节点之间的关系,最后split,分割变成两个链表
// 这次是带有随机指针节点rand的链表
public static class Node {
public int value;
public Node next;
public Node rand;
public Node(int data) {
this.value = data;
}
}
// 第一种是利用外部辅助--hashMap,需要O(N)空间复杂度
public static Node copyListWithRandom01(Node head) {
// 1、给出一个辅助hashMap,其键值对都是Node类型
HashMap<Node, Node> hashMap = new HashMap<Node, Node>();
// 2、将源链表当作键存入hashmap中,每个节点对应一个复制(新)节点
Node cur = head; // 老规矩,用临时节点代替头节点,因为头节点下面可能要用,最好不要改变
while (cur != null) { // 遍历链表
hashMap.put(cur, new Node(cur.value)); // 这里就很巧妙,一个源链表的节点对应一个复制链表的节点,节点间的关系下面会给出
cur = cur.next;
}
// 3、从hashmap里面取数,建立我们复制链表节点间的关系
cur = head; // 重制cur,省一个变量
while (cur != null) {
hashMap.get(cur).next = hashMap.get(cur.next); // 看的出来么,很巧妙,当前值的next就是键的next所代表的数
hashMap.get(cur).rand = hashMap.get(cur.rand);
}
return hashMap.get(head);
}
// 第二种就是O(1),1 > 1_ > 2 > 2_ > 3 > 3_ > null ,只用变量,空间复杂度为O(1)
public static Node copyListWithRandom02(Node head) {
if (head == null) {
return head;
}
Node cur = head;
Node next = null;// 搞一个用于交换的节点
// 1、将1 > 2 > 3 > null变成1 > 1_ > 2 > 2_ > 3 > 3_ > null
while (cur != null) {
next = cur.next; // 先把2存储一下
cur.next = new Node(cur.value);// 让cur指向我们复制的cur节点,即上面的1_
cur.next.next = next; // 这个复制的节点指向2
cur = next; // 调整头节点的位置,继续遍历
}
// 2、调整复制链表节点之间的关系
cur = head;
Node copNode;
while (cur != null) {
next = cur.next.next;
copNode = cur.next;
// 因为copNode的next还是没有变,这里只要把rand设置一下就行了
copNode.rand = cur.rand == null ? null : cur.rand.next;
cur = next; // 调整cur,继续遍历
}
// 3、split 1 > 1_ > 2 > 2_ > 3 > 3_ > null,变成两个链表
cur = head;
Node res = head.next;
while (cur != null) {
next = cur.next.next;
copNode = cur.next;
cur.next = next;
copNode.next = next == null ? null : next.next;
cur = next;
}
return res;
}
// 打印链表,这里按照next和rand各打印了一次
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) {
// 和上面唯一不一样的就是这里,因为rand可能会指向null,所以这里弄个选择语句,指空就输出"- "
System.out.print(cur.rand == null ? "- " : cur.rand.value + " ");
cur = cur.next;
}
System.out.println();
}
public static void main(String[] args) {
// 测试头节点为null的情况
Node head = null;
Node res1 = null;
Node res2 = null;
printRandLinkedList(head);
res1 = copyListWithRandom01(head);
printRandLinkedList(res1);
res2 = copyListWithRandom02(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 = copyListWithRandom01(head);
printRandLinkedList(res1);
res2 = copyListWithRandom02(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