剑指offer.35.复杂链表的复制

前言

剑指offer打卡,35题,复杂链表的复制,同样可以利用链表插入和摘节点的快速操作来完成

一、HashMap一一对应

1、思想

1)针对next指针
遍历链表,生成Node节点,并把原始节点和生成的节点做一个hashMap的<key,value>映射,也就是原Node作为Key,新对应的Node作为Value。
2)针对random指针
再次遍历链表,通过原链表的Node的random指针所指的节点作为Key,通过map查出已有的新节点Value。然后给新节点的random指针赋值。

2、源码

public class CopyRandomList35 {
	//记录旧节点和新节点的一一对应
    Map<Node, Node> existN = new HashMap<>();
    public Node copyRandomList(Node head) {
        Node point = head;
        if (head == null)
            return null;
        Node cnode = new Node(head.val);
        Node result = cnode;
        existN.put(head, cnode);
        Node node;
        //第一遍遍历为了Next指针和用Map记录顺序
        while (point.next != null) {
            node = new Node(point.next.val);
            existN.put(point.next, node);
            cnode.next = node;
            cnode = node;
            if (existN.get(point.next.random) != null) {
                node.random = existN.get(point.next.random);
            }
            point = point.next;
        }
        point = head;
        cnode = result;
        //第二遍循环通过Map的记录来给新节点的Random指针赋值
        while (point != null) {
            if(point.random != null && cnode.random == null) {
                cnode.random = existN.get(point.random);
            }
            point = point.next;
            cnode = cnode.next;
        }
        return result;
    }
}

// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}

二、无Map用链表自身特性

1、思想

第一种算法要用map记录新旧对应,需要额外的空间。利用链表自身插入和删除快速的特点,采用第一次遍历先复制旧节点到其后面,然后再次遍历将random指针设好,最后一次遍历来一分为二。

2、源码

// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
class CopyRandomList {
    public Node copyRandomList(Node head) {
        Node point = head;
        if (head == null)
            return null;
        Node node;
        //复制相同的节点在原节点后面。
        while (point != null) {
            node = new Node(point.val);
            node.next = point.next;
            point.next = node;
            point = point.next.next;
        }
        point = head;
        Node result = point.next;
        //设置random指针
        while (point != null) {
            if(point.random!=null){
                point.next.random = point.random.next;
            }
            point = point.next.next;
        }
        point = head;
        //一分为二
        while(point.next.next != null){
            Node node1 = point.next;
            point.next = point.next.next;
            node1.next = point.next.next;
            point = point.next;
        }
        point.next = null;
        return result;
    }
}

三、总结

Map有快速查找的功能,链表有记录顺序且删除插入都非常方便的特点。利用好各种数据结构的特性,联系到题目,便能快速解题。

补充

看了官方的解答,第一种叫回溯+hash表,这是我方法一的一种简化,第一次看到回溯的使用方法,原来是通过递归来回溯达到快速且简洁。

1、源码

class Solution {
    Map<Node, Node> cachedNode = new HashMap<Node, Node>();

    public Node copyRandomList(Node head) {
        if (head == null) {
            return null;
        }
        if (!cachedNode.containsKey(head)) {
            Node headNew = new Node(head.val);
            cachedNode.put(head, headNew);
            headNew.next = copyRandomList(head.next);
            headNew.random = copyRandomList(head.random);
        }
        return cachedNode.get(head);
    }
}

这里通过递归先完成next节点的创建,然后递归回来的时候,map已经记录好了,这个时候就直接使用map的value来设置random指针。这样一来一回就完成了我三面的三步。
所以headNew.random = copyRandomList(head.random);可以改成headNew.random = cachedNode.get(head.random);。这样容易理解。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值