复杂链表的复制

题目描述

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的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)
    {
      
    }
}

思路一

image

想要复制这样一个链表,最容易想到的方法是先复制普通的链表,然后复制派生出来的节点关系。

时间复杂度: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节点的查找和遍历以及复制)

image


    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

image

然后复制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

第三步进行拆分,其中奇数为原来的链表,偶数为复制后的链表

image

image

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判断,不是很明白其中的道理😥

posted @ 2019-02-22 15:35  crr121  阅读(141)  评论(0编辑  收藏  举报