【题目】
一种特殊的链表节点类描述如下:
public class Node { public int value; public Node next; public
Node rand;
public Node(int data) { this.value = data; }
}
Node类中的value是节点值,next指针指向下一个节点,rand指针可能指向链表中的任意一个节点,也可能指向null。
给定一个由Node节点类型组成的无环单链表的头节点head,请实现一个函数完成这个链表中所有结构的复制,并返回复制的新链表的头节点。
【进阶】
不使用额外的数据结构,只用有限几个变量,且在时间复杂度为 O(N)内完成原问题要实现的函数。
【分析】
我们可以用hashMap存储键值对来时间链表结构的复制。(需要哈希表,所以有额外的数据空间)
1.遍历链表中所有的节点,作为hashMap中的键存进去;
2.hashMap中的值就是对应键的复制节点;
3.我们复制的结构是:通过键(原来的节点)找到值(复制的节点);
通过原来链表中的指向关系找到下一个结点;
把这个节点作为键找到对应的值(就是后一个的复制节点),建立指向关系。
4.随机指针同理。
【代码实现】
public static Node copyListWithRand1(Node head) {
HashMap<Node, Node> map=new HashMap<>();
Node cur=head;
while(cur!=null) {
map.put(cur, new Node(cur.value));
cur=cur.next;
}
cur=head;
while(cur!=null) {
map.get(cur).next=map.get(cur.next);
map.get(cur).rand=map.get(cur.rand);
}
return map.get(head);
}
【进阶分析】
1.每两个节点之间插入前一个结点的复制节点
2.复制节点的随机节点指向问题
3.断开原链表和复制链表
【代码实现】
public static Node copyListWithRand2(Node head) {
if (head==null) {
return null;
}
Node cur=head;
//next用来保存的是当前操作节点的下一个结点
Node next=null;
//1.完成复制结点和next指针指向
while(cur!=null) {
next=cur.next;//保存住当前操作节点的下一个结点
cur.next=new Node(cur.value);//当前操作节点指向他的复制节点
cur.next.next=next;//当前操作节点的复制节点指向的是原来next保留住的节点
cur=next;//移动,进行下一个结点的操作
}
//2.完成随机指针指向
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;//进行下一个节点的操作
}
//3.完成原链表和复制链表的分裂
Node res=head.next;//第一个复制节点,同时也是要返回的值
cur=head;
while(cur!=null) {
next=cur.next.next;//保存住原来链表的下一个结点
curCopy=cur.next;//保存住复制节点
cur.next=next;//原来节点指向原链表中他的下一个结点
curCopy.next=next!=null?next.next:null;//原来节点的复制节点指向的是原来下一个结点的下一位
cur=next;//移动当前操作指针。
}
return res;
}