(算法笔记)(链表)复制含有随机指针节点的链表

复制含有随机指针节点的链表
【题目】一种特殊的单链表节点类描述如下

class Node {
    int value;
    Node next;
    Node rand;
    Node(int val) {
        value = val ;
    }
}

rand指针是单链表节点结构中新增的指针,rand可能指向链表中的任意一个节点,也可能指向null。给定一个由Node节点类型组成的无环单链表的头节点head,请实现一个函数完成这个链表的复制,并返回复制的新链表的头节点。【要求】时间复杂度O(N),额外空间复杂度0(1)

采用一般的方法直接复制,

问题:因为直接复制next和rand会指向原链表的node地址,而不是新链表的node的地址

方法一(空间复杂度为O(n),笔试中使用能帮助尽快解题):

思想:

  1. 采用一个Hash表,其key值放老结点的内存地址,value放入从老结点中复制得来的新结点内存地址,当结点遍历完毕后,所有的新旧结点均拷贝完毕;
  2. 从头遍历给定的但链表,每遍历一个结点,就得出他的rand和next指针,然后因为next指针和rand指针都是作为Hash表key存在,所以可以依据这个next和rand指针经由hash函数得出其对应新结点的所在地址(假设为N和R),再根据当前结点和哈希函数,得出当前结点的新结点,然后再设置新结点的next和rand指针分别指向N和R
  3. 重复步骤二,直到链表便利完毕

代码

package ListNode;

import Pojo.ListNode;

import java.util.HashMap;

public class CopyNodeWithRand {

    static class Node {
        int value;
        Node next;
        Node rand;
        Node(int val) {
            value = val ;
        }
    }
    public static void main(String[] args) {

    }

    public static Node copyNodeWithRand(Node head)
    {
        HashMap<Node, Node> map = new HashMap<>();
        Node cur = head;
        // 将链表所有结点的值放到其对应的新结点中并且将这个新结点作为hashMap的value,旧结点作为map的key
        while (cur != null)
        {
            map.put(cur, new Node(cur.value));
            cur = cur.next;
        }
        cur =head;
        while (cur != null)
        {
            // 设置当前结点对应新结点的random和next指针
            map.get(cur).next = map.get(cur.next);
            map.get(cur).rand = map.get(cur.rand);
        }
        return map.get(head);
    }
}

方法二(空间复杂度为O(1),面试中使用能体现出你的水平)

思想:

该思想主要运用新旧节点之间的位置关系省略了方法一中的额外的hash表空间

  1. 为每一个结点生成一个克隆结点,克隆结点只包含原结点的data值,将其作为对应老结点的下一个结点。比如对于链表 1 -> 2 -> 3 -> null,其中1的rand域指向3,2的rand域指向1;按照该规则,链表则变化为 1 -> 1’ -> 2 -> 2’ -> 3 -> 3’ -> null;
  2. 采用双指针pre和cur,pre始终指向cur的前一个结点,cur为当前遍历结点,当cur遍历到克隆节点时,比如1’,则pre指向该克隆节点的原结点,此时通过pre.rand.next即可得到cur结点(即1’)的rand指针指向的地址,随后cur跳到下一个旧结点(指2),如此直到遍历到链表空结点。
  3. 将新的链表进行撕裂,分成两个链表,一个全部由新结点组成,另一个全部由旧结点组成,然后返回全部由克隆节点组成的链表表头

代码:

  // 不使用额外空间,只利用链表新老结点之间的顺序特征进行复制
    public static Node copyNodeWithRand2(Node head)
    {
        Node cur =head;
        // 为每一个结点生成一个克隆结点,克隆结点只包含原结点的data值,将其作为对应老结点的下一个结点。
        // 比如对于链表 1 -> 2 -> 3 -> null,其中1的rand域指向3,2的rand域指向1;按照该规则,
        // 链表则变化为 1 -> 1' -> 2 -> 2' -> 3 -> 3' -> null
        while (cur != null)
        {
            Node next = cur.next;
            cur.next = new Node(cur.value);
            cur.next.next = next;
            cur = cur.next;
        }
        // copy rand pointer of each old node
        cur = head;
        while (cur!= null)
        {
            Node next_next = cur.next.next;// cur每次跳两步
            Node curCopy = cur.next;
            curCopy.rand = cur.rand != null ? cur.rand.next : null;
            cur = next_next;
        }
        // split
        cur = head;
        Node response = head.next;
        while (cur != null)
        {
            Node next_next = cur.next.next; // cur每次跳两步
            Node curCopy = cur.next;
            curCopy.next = next_next.next; // 新结点连接到新结点
            cur.next = next_next != null ? next_next : null; // 旧结点连接到旧结点
            cur = cur.next; // 遍历下一个旧结点
        }
        return response;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值