一个链表的数据结构如下
public class ListNode {
public int val;
public ListNode next=null;
public ListNode rand=null;
ListNode(int val){ //构造方法 :构造方法和类名相同
this.val=val; //把接收的参数赋值给当前类的val变量
}
}
问题:如果一个链表的next节点指向它的下一个节点,它的rand节点指向随机节点(可以为null),那么我们应该怎么复制这个链表。
首先我们应该知道如果普通的for循环直接每次创建一个节点来赋值创建肯定是不行的,因为rand节点是指向随机节点的一个值。那么第一个解法:
先循环用hashMap保存每一个创建的节点信息,然后再循环修改值。(时间复杂度为O(n) 空间复杂度为O(n))。
代码如下:
public static ListNode copyListWithRand(ListNode head){
ListNode node=head;
HashMap<ListNode,ListNode> hashMap=new HashMap<>();
while(node!=null){//先复制节点 存入map中
hashMap.put(node,new ListNode(node.val));
node=node.next;
}
node=head;
while(node!=null){//再次遍历进行节点的赋值
ListNode temp=hashMap.get(node);
temp.next=node.next;
temp.rand=node.rand;
node=node.next;
}
return hashMap.get(head);//返回头节点
}
但是如果我们想要时间复杂度为O(n),空间复杂度O(1)的情况下完成这个算法应该怎么办,循环的时候创建节点直接放到我们被复制节点的后面,如图:
复制完过后我们再次遍历进行操作,首先将1节点的next指针指向2,然后将被赋值节点 1’ 的next指针指向2’就是 1.next.next,然后将 1’的rand指针指向 1的rand的next节点。直接看代码:
public static ListNode copyListWithRandBetter(ListNode head){
ListNode node=head;
while(node!=null){//先创建节点
ListNode nodeTemp=new ListNode(node.val);
nodeTemp.next=node.next;
node.next=nodeTemp;
node=nodeTemp.next;
}
node=head;
ListNode result=node.next;//保存要返回节点的头节点
while(node!=null){
ListNode nodeTemp=node;
ListNode nodeTemp2=node.next;
nodeTemp.next=nodeTemp2.next;//将node节点指针复原 然后给复制节点进行赋值操作
nodeTemp2.next=nodeTemp.next==null?null:nodeTemp.next.next;//node的next节点可能指向空
nodeTemp2.rand=nodeTemp.rand==null?null:nodeTemp.rand.next;//node的rand节点可能指向空
node=node.next;
}
return result;//返回头节点
}