复杂链表的复制
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
/*
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
*/
public class Solution {
public RandomListNode Clone(RandomListNode pHead)
{
}
}
思路一
想要复制这样一个链表,最容易想到的方法是先复制普通的链表,然后复制派生出来的节点关系。
时间复杂度:o(n^2)
import java.util.ArrayList;
public class Test8 {
public static class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
//已知当前节点的ramdomnode,寻找ramdomnode在整个普通链表中的位置索引
public int findRandomIndex(RandomListNode ramdomNode,RandomListNode head){
int index = 0;
while (head != null){
if(ramdomNode == head) return index;
head = head.next;
index++;
}
return index;
}
//返回每个节点的ramdom分支节点的位置索引数组
public ArrayList<Object> randomIndexArray(RandomListNode head){
RandomListNode headCon = head;
ArrayList<Object> indexList = new ArrayList<>();
Object randomIndex = 0;
while (head != null){
if(head.random != null){
randomIndex = findRandomIndex(head.random, headCon);
}else {
randomIndex = null;
}
indexList.add(randomIndex);
head = head.next;
}
return indexList;
}
//通过index寻找节点
public RandomListNode findNodeByIndex (RandomListNode head,int index){
int j = 0;
while (head != null){
if(index == j){
return head;
}else {
j++;
head = head.next;
}
}
return head;
}
public RandomListNode Clone(RandomListNode pHead)
{
if(pHead == null) return null;
RandomListNode pCur = pHead;
RandomListNode pNew = null;
int count = 0;
//复制普通链表
while(pCur != null){
RandomListNode node = new RandomListNode(pCur.label);
node.next = pCur.next;
if(count == 0) pNew = node;
pCur = pCur.next;
count++;
}
//复制分支节点链表
pCur = pHead;
ArrayList<Object> indexArray = randomIndexArray(pCur);
RandomListNode newHead = pNew;
while (pNew != null){
for(int i = 0; i < indexArray.size(); i++){
if(indexArray.get(i) != null){
int index = (int) indexArray.get(i);
RandomListNode ramdomNode = findNodeByIndex(pCur, index);
pNew.random = ramdomNode;
}
pNew = pNew.next;
}
}
return newHead;
}
public static void main(String[] args){
RandomListNode head = new RandomListNode(1);
RandomListNode node2 = new RandomListNode(2);
RandomListNode node3 = new RandomListNode(3);
RandomListNode node4 = new RandomListNode(4);
RandomListNode node5 = new RandomListNode(5);
head.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
head.random = node3;
node2.random = node5;
node4.random = node2;
RandomListNode head1 = new Test8().Clone(head);
while (head1 != null){
System.out.println(head1.label);
if(head1.random == null) head1.random = new RandomListNode(0);
System.out.println("head1.random.label=>"+head1.random.label);
head1 = head1.next;
}
}
}
输出结果:
1
head1.random.label=>3
2
head1.random.label=>5
3
head1.random.label=>0
4
head1.random.label=>2
5
head1.random.label=>0
可以看出该方法的时间复杂度很高,O(n2),因为有两层while循环
思路二(用空间换时间)
用map存储,将原来的链表看作key,将复制后的链表看作value,通过key-value一一对应起来,
然后同样分为两步进行复制,先复制普通的链表节点,然后复制分支ramdom节点,复制random节点的时候就不用重新遍历了,key对应的random就是value对应的random节点
时间复杂度:o(n)
空间复杂度:o(n)
public RandomListNode Clone(RandomListNode pHead)
{
if (pHead == null)
return pHead;
HashMap<RandomListNode, RandomListNode> map = new HashMap<>();
//复制单独的节点
//这里采用for循环,就可以不会破坏pHead的值,如果采用while循环就需要提前复制pHead的值
for (RandomListNode p = pHead; p != null; p = p.next) {
map.put(p, new RandomListNode(p.label));
}
//复制普通连接关系+随机连接关系 通过map的key-value对应关系省略了遍历的时间
for (RandomListNode p = pHead; p != null; p = p.next) {
map.get(p).next = map.get(p.next);
map.get(p).random = map.get(p.random);
}
return map.get(pHead);
}
resolution2:while循环遍历节点
/*
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
*/
//注意学会自己添加头文件
import java.util.HashMap;
public class Solution {
public RandomListNode Clone(RandomListNode pHead)
{
HashMap<RandomListNode, RandomListNode> map = new HashMap<>();
//提前复制
RandomListNode head = pHead;
RandomListNode newHead = head;
while(pHead != null){
map.put(pHead,new RandomListNode(pHead.label));
pHead = pHead.next;
}
while(head != null){
map.get(head).next = map.get(head.next);
map.get(head).random = map.get(head.random);
head = head.next;
}
//返回头节点
return map.get(newHead);
}
}
思路三
在不增加空间复杂度的情况下,降低时间复杂度,那么主要就是需要减少查找random节点的时间,如果我们将random节点能够缩小到O(n)的时间复杂度,那么新的链表的random节点一定是和原来的节点有关联的,思路二我们采用了map的key-value进行关联,那么思路三我们采用相邻节点进行关联
时间复杂度:o(n)
首先同样是复制普通节点,这里采用相邻复制(便于后面的random节点的查找和遍历以及复制)
public RandomListNode comClone(RandomListNode pHead){
RandomListNode head = pHead;
//复制普通节点(两两相邻)
while (pHead != null){
RandomListNode temp = pHead.next;
pHead.next = new RandomListNode(pHead.label);
pHead.next.next = temp;
pHead = temp;
}
return head;
}
1
head1.random.label=>3
1
head1.random.label=>0
2
head1.random.label=>5
2
head1.random.label=>0
3
head1.random.label=>0
3
head1.random.label=>0
4
head1.random.label=>2
4
head1.random.label=>0
5
head1.random.label=>0
5
head1.random.label=>0
然后复制random节点
public RandomListNode ranClone(RandomListNode head){
RandomListNode head_1 = head;
while (head != null){
if(head.random != null){
head.next.random = head.random.next;
}
head = head.next.next;
}
return head_1;
}
1
head1.random.label=>3
1
head1.random.label=>3
2
head1.random.label=>5
2
head1.random.label=>5
3
head1.random.label=>0
3
head1.random.label=>0
4
head1.random.label=>2
4
head1.random.label=>2
5
head1.random.label=>0
5
head1.random.label=>0
第三步进行拆分,其中奇数为原来的链表,偶数为复制后的链表
public RandomListNode reconnectNodes(RandomListNode pHead){
RandomListNode pNode = pHead;
RandomListNode pCloneHead = null;
RandomListNode pCloneNode = null;
//设置第一个节点,便于后面的指针处理,防止空指针的出现
if(pNode != null){
pCloneHead = pCloneNode = pNode.next;
pNode.next = pCloneNode.next;
pNode = pNode.next;
}
while (pNode != null){
pCloneNode.next = pNode.next;
pCloneNode = pCloneNode.next;
pNode.next = pCloneNode.next;
pNode = pNode.next;
}
return pCloneHead;
}
错误方法:
public RandomListNode reconnectNodes(RandomListNode pHead){
RandomListNode pNode = pHead;
RandomListNode pCloneHead = pNode.next;
RandomListNode pCloneNode = pCloneHead;
pNode = pNode.next;
while (pNode != null){
pCloneNode.next = pNode.next;
pCloneNode = pCloneNode.next;
pNode.next = pCloneNode.next;
pNode = pNode.next;
}
return pCloneHead;
}
该方法不能通过所有的测试用例
用例:
{1,2,3,4,5,3,5,#,2,#}
对应输出应该为:
{1,2,3,4,5,3,5,#,2,#}
你的输出为:
java.lang.NullPointerException
这里只是将初值的设置单独拿出来了,没有用if判断,不是很明白其中的道理😥
欢迎关注我的公众号:小秋的博客
CSDN博客:https://blog.csdn.net/xiaoqiu_cr
github:https://github.com/crr121
联系邮箱:rongchen633@gmail.com
有什么问题可以给我留言噢~