zoukankan      html  css  js  c++  java
  • 剑指Offer_#35_复杂链表的复制

    剑指Offer_#35_复杂链表的复制

    Contents

    题目

    请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
    例如:
    enter description here
    提示:

    • -10000 <= Node.val <= 10000
    • Node.random 为空(null)或指向链表中的节点。
    • 节点数目不超过 1000 。

    思路分析

    方法1:哈希表

    1. 构建哈希表
      沿着next指针,逐个遍历旧链表节点,克隆出一个新链表节点。向哈希表添加<旧节点,新节点>的键值对。
    2. 构造新链表
      从头节点开始,遍历旧链表节点,然后通过哈希表获得旧节点所对应的新节点,以及旧节点的next节点/random节点所对应的新节点。设置每个新节点的nextrandom

    方法2:原地修改链表

    1. 在每个节点后克隆一个新节点
    2. 设置新节点的random指针,指向应该指向的位置
    3. 修改next指针,将拷贝的链表分离出来

    图解

    enter description here

    解答

    解答1:哈希表

    class Solution {
        public Node copyRandomList(Node head) {
            HashMap<Node,Node> map = new HashMap<>();
            Node cur = head;
            while(cur != null){
                map.put(cur,new Node(cur.val));
                cur = cur.next;
            }
            cur = head;
            while(cur != null){
                map.get(cur).next = map.get(cur.next);
                map.get(cur).random = map.get(cur.random);
                cur = cur.next;
            }
            return map.get(head);
        }
    }

    复杂度分析

    时间复杂度O(n)
    空间复杂度O(n)

    解答2:原地修改链表

    class Solution {
        public Node copyRandomList(Node head) {
            CloneNodes(head);
            ConnectRandomNodes(head);
            return ReconnectNodes(head);
        } 
        //在每个原节点之后增加一个克隆的节点
        public void CloneNodes(Node head){
            Node cur = head;
            while(cur != null){
                Node cloned = new Node(cur.val);
                cloned.next = cur.next;
                cur.next = cloned;
                cur = cloned.next;
            }
        }
        //设置每个新节点的random指针
        public void ConnectRandomNodes(Node head){
            Node cur = head;
            while(cur != null){
                //当前节点的下一个节点是克隆节点
                Node cloned = cur.next;
                if(cur.random != null) cloned.random = cur.random.next;
                //每次迭代,指针移动两个节点
                cur = cloned.next;
            }
        }
        //将拷贝的链表拆分出来
        public Node ReconnectNodes(Node head){
            //cur指针指向旧链表的节点
            Node cur = head;
            //ERROR:必须对cloneNode和cloneHead在if之外进行初始化,否则可能不被初始化
            //clonedNode是新链表的指针
            Node clonedNode = null;
            //clonedHead是新链表头节点
            Node clonedHead = null;
            if(cur != null){
                clonedHead = cur.next;      
                clonedNode = clonedHead; 
                //cur指针先走一步,这样才方便连接克隆的节点
                cur.next = clonedNode.next;
                cur = cur.next;
            }
            //cur非null,那么cloneNode一定也非null
            while(cur != null){
                //连接到下个克隆节点
                clonedNode.next = cur.next;
                //更新cloneNode指针
                clonedNode = clonedNode.next;
                //连接到下个旧链表节点
                cur.next = clonedNode.next;
                //更新cur指针
                cur = cur.next;
            }
            return clonedHead;
        }     
    }

    复杂度分析

    时间复杂度O(n)
    空间复杂度O(1)

    TIP:避免链表迭代中的空指针异常

    比较容易出现的问题是空指针异常,防止这个异常的方式就是增加一个条件判断。具体举例说明:

    • 因为要访问cur.random.next,所以必须保证cur.random不是null
    if(cur.random != null) cloned.random = cur.random.next;
    • 因为要访问cur.next,所以必须保证cur不是null
    if(cur != null){
                clonedHead = cur.next;      
                clonedNode = clonedHead; 
                //cur指针先走一步,这样才方便连接克隆的节点
                cur.next = clonedNode.next;
                cur = cur.next;
            }

    也就是说,每次些链表循环的代码,一定要注意循环过程中,*.next或者*.val之类的语句,一定要再其之前增加条件判断语句if(* != null)

  • 相关阅读:
    STM32低功耗模式与烟雾报警器触发信号电路设计
    cocos2d-x的环境的搭建
    window8.1中用户的管理员权限的提升方法
    cmd中目录的变更
    js的传值,table中tr的遍历,js中动态创建数组
    究竟什么是游戏引擎?
    大型网站架构学习心德
    关于listView 中的聚焦问题
    android6.0 适配的问题——activity销毁的问题
    文件发送成功率低的问题(2)
  • 原文地址:https://www.cnblogs.com/Howfars/p/13275735.html
Copyright © 2011-2022 走看看