题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
最暴力的方法:首先利用next指针将复制的节点串起来,然后在每一个节点处,去寻找random指针指向哪,然后在复制的链表中找到对应的节点。因为每次去寻找指针都会从头移动O(n),而又有n个节点,因此时间复杂度是O(n^2)
有两种改进方式。
改进1:一种是空间换时间,在复制节点时,用map保存旧节点和新节点之间的映射关系。然后分别复制next和random,一共两轮循环,所以时间复杂度是O(n),空间复杂度是O(n)
import java.util.HashMap; public class Solution { public RandomListNode Clone(RandomListNode pHead) { if (pHead == null) { return pHead; } RandomListNode p1 = pHead; //p1是指向旧链表的指针,将新旧节点对应 RandomListNode p2 = pHead; //p2是指向新链表的指针 HashMap<RandomListNode, RandomListNode> map = new HashMap<>(); while (p1 != null) { //存放的是p1节点和一个新节点,这个新节点的值和p1的值相同 //通俗来看,这一步是将节点复制,但是没有串起来(复制好了每一个颗糖葫芦,但是没串到竹签上) RandomListNode clone = new RandomListNode(p1.label); map.put(p1, clone); p1 = p1.next; } while (p2 != null) {//p2在原链表上移动 //map.get(p2)通过键值对,拿到了新链表在p2位置的节点应该是哪一个 //map.get(p2).next= 表示这个新链表的p2位置节点的下一个节点应该指向? //map.get(p2.next) 表示拿到原链表p2位置节点指向的下一个节点 //这个赋值通俗来就是,根据原来的糖葫芦顺序,把复制的每一颗的糖葫芦依葫芦画瓢的串起来 if (p2.next != null) { map.get(p2).next = map.get(p2.next); } else { map.get(p2).next = null; } //这里就是进行random指针的复制,这里的random和next的复制顺序可以交换,p2=p2.next必须在最后 map.get(p2).random = map.get(p2.random); p2 = p2.next; } return map.get(pHead); } }
改进2:不用O(n)的空间复杂度,依旧实现时间复杂度是O(n)
来自Cyc20***佬的图
public RandomListNode Clone(RandomListNode pHead) { if (pHead == null) return null; // 插入新节点(插入节点的写法),同时建立了next连接 RandomListNode cur = pHead; while (cur != null) { RandomListNode clone = new RandomListNode(cur.label); clone.next = cur.next; cur.next = clone; cur = clone.next; } // 建立 random 链接(从头开始) cur = pHead; while (cur != null) { RandomListNode clone = cur.next; if (cur.random != null) clone.random = cur.random.next; cur = clone.next; } // 拆分 //拆分的细节需要注意 a->a1->b->b1->c->c1 cur = pHead;//当前指针指向a RandomListNode pCloneHead = pHead.next;//一个指向a1的指针 while (cur.next != null) {//第一个whlie的功能仅仅是a->a1变成a->b。 RandomListNode next = cur.next;//next是指向a1的指针 cur.next = next.next;//a将会与b连接(a不再指向a1,而是b了,就这一处改动) cur = next;//指针指向a1,以便下次进行a1到b的拆分(完成了一次拆分) } //每一次whlie,都会改变一次连接(改变一个箭头指向,而不是多个箭头)。 //第一次将a与b连接,第二次将a1与b1连接,第三次是将b与c连接.... return pCloneHead; }