先上题目
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
RandomListNode类
public class RandomListNode {
int label;//节点值
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
思路
两种解题思路,第一种使用map来保存新旧两个链表,第二种是先把链表复制一遍,然后再拆分成两个。
1、map方法
用map存放新旧两个链表,由于复制链表有一个random指针指向随机节点,因此当我们追踪某个节点的next时,可能这个next是之前某个节点的random,已经被存入map中了,所以就不用再存入,了解这种情况后代码看起来就比较清晰了。上代码
public class Clone {
private static RandomListNode solution(RandomListNode pHead){
//判空
if (pHead == null) return null;
//用来存放两个链表的map,key为旧链表,value为新链表
Map<RandomListNode, RandomListNode> map = new HashMap<>();
//新链表的头
RandomListNode newHead = new RandomListNode(pHead.label);
//追踪旧链表的指针
RandomListNode point = pHead;
//追踪新链表的指针
RandomListNode newPoint = newHead;
while (point!=null){
//追踪next
if (point.next!=null && map.containsKey(point.next)){
newPoint.next = map.get(point.next);
}else {
//先判断next是否存在
if (point.next!=null){
//这里要生成一个新的节点,否则直接temp=point.next的话next节点的next信息也带在其中,题目就没有意义了
RandomListNode temp = new RandomListNode(point.next.label);
map.put(point.next, temp);
newPoint.next=temp;
}
}
//追踪random
if (point.random!=null && map.containsKey(point.random)){
newPoint.random = map.get(point.random);
}else {
//先判断random是否存在
if (point.random!=null){
//同样要生成新节点
RandomListNode temp = new RandomListNode(point.random.label);
newPoint.random = temp;
map.put(point.random, temp);
}
}
//两个指针都指向下一个
point = point.next;
newPoint = newPoint.next;
}
return newHead;
}
}
2、复制解法
1、先将链表每个节点复制一次,复制出来的节点插到原节点的后边
2、遍历链表,给复制出来的节点赋上random
3、将链表拆分成两个
如果理解不了可以自己画个图,这里第3步只举了一个while循环
private static RandomListNode solution(RandomListNode pHead){
//判空
if (pHead == null) return null;
//用来追踪的指针
RandomListNode point = pHead;
//新链表的头,最后返回用到
RandomListNode newHead = null;
//1、复制链表
while (point!=null){
//克隆出当前节点
RandomListNode cloneNode = new RandomListNode(point.label);
//把下一个节点提取出来
RandomListNode next = point.next;
//将克隆出来的节点插入
point.next = cloneNode;
cloneNode.next=next;
//指向下一个节点
point=next;
}
//指针回到头结点
point = pHead;
//2、遍历链表,给复制出来的链表插上random
while (point!=null){
if (point.random!=null){
//这里有个坑,一开始没注意到困惑了很久
//注意这里的point.next.random要指向point.random.next!!, 这个next不能掉,否则就带有random节点的信息了。跟map解法中要生成新的节点一个道理
point.next.random = point.random.next;
}else {
point.next.random = null;
}
point = point.next.next;
}
//指针回到头结点
point = pHead;
//3、拆分两个链表
newHead = pHead.next;
while (point!=null){
//属于新链表的节点
RandomListNode cloneNode = point.next;
//属于旧链表的节点,cloneNode.next就是他原本的下一个节点
point.next = cloneNode.next;
//这里要进行判空,否则会报错
cloneNode.next = cloneNode.next==null? null : cloneNode.next.next;
point = point.next;
}
return newHead;
}