其他分享
首页 > 其他分享> > leetcode刷题记录day035:138和430

leetcode刷题记录day035:138和430

作者:互联网

138、难度中等:

方法一:原创:暴力哈希表:

思路:有三点有解决的:
①、构建所有的next:第一轮循环从头开始遍历
②、每个next和random都不能是原先链表的节点:创建新链表节点时采用ano.next = new Node(head.next.val,null,null);方式
③、构建所有的random:创建哈希表,第一轮循环时加入原链表和新链表各节点的逐个对应关系,第二轮循环从头开始遍历,从哈希表中取出新链表的节点,采用ano.random = con.get(ori.random);方式

/*
// 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 Solution {
    public Node copyRandomList(Node head) {
        if(head == null){
            return null;
        }
        // 创建对应题传链表和新链表各个节点的哈希表
        Map<Node, Node> con = new HashMap<Node, Node>();
        // 用于构建新链表
        Node ano = new Node(head.val,null,null);
        // 加入对应关系
        con.put(head,ano);
        // 作为返回结果用
        Node ans = ano;
        // 用于第二轮循环加入random时用,保存原链表头结点 
        Node ori = head;
        // 第一轮循环加入所有next
        while(head.next != null){
            ano.next = new Node(head.next.val,null,null);
            con.put(head.next,ano.next);
            ano = ano.next;
            head = head.next;
        }
        // 第二轮循环加入random
        ano = ans;
        while(ano != null){
            ano.random = con.get(ori.random);
            ano = ano.next;
            ori = ori.next;
        }
        
        return ans;
    }
}
方法二:回溯 + 哈希表:

思路:本题中因为随机指针的存在,当我们拷贝节点时,「当前节点的随机指针指向的节点」可能还没创建,因此我们需要变换思路。
我们用哈希表记录每一个节点对应新节点的创建情况,若当前节点不再哈希表中,我们首先要进行拷贝,然后我们进行「当前节点的后继节点」和「当前节点的随机指针指向的节点」拷贝(我们检查「当前节点的后继节点」和「当前节点的随机指针指向的节点」的创建情况。如果这两个节点中的任何一个节点的新节点没有被创建,我们都立刻递归地进行创建。)
,拷贝完成后将创建的新节点的指针返回,即可完成当前节点的两指针的赋值。

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

    public Node copyRandomList(Node head) {
        if (head == null) {
            return null;
        }
        // 从第一个节点开始处理,哟哟与第一个节点的next和random还未被创建出来,所以递归的调用方法本身
        // 去创建next和random,又由于调用方法本身又会创建next、random各自的next和random
        // 这样最后就复制完了链表
        // 如何确保递归会停止,哈希表存储哪些结点已经被创建完了
        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);
    }
}
/**
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/copy-list-with-random-pointer/solution/fu-zhi-dai-sui-ji-zhi-zhen-de-lian-biao-rblsf/
**/
方法三:迭代 + 节点拆分:空间复杂度O(1)

具体原理点击下方链接:根据图解直接看代码就能看懂
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/copy-list-with-random-pointer/solution/fu-zhi-dai-sui-ji-zhi-zhen-de-lian-biao-rblsf/

class Solution {
    public Node copyRandomList(Node head) {
        if (head == null) {
            return null;
        }
        for (Node node = head; node != null; node = node.next.next) {
            Node nodeNew = new Node(node.val);
            nodeNew.next = node.next;
            node.next = nodeNew;
        }
        for (Node node = head; node != null; node = node.next.next) {
            Node nodeNew = node.next;
            nodeNew.random = (node.random != null) ? node.random.next : null;
        }
        Node headNew = head.next;
        for (Node node = head; node != null; node = node.next) {
            Node nodeNew = node.next;
            node.next = node.next.next;
            nodeNew.next = (nodeNew.next != null) ? nodeNew.next.next : null;
        }
        return headNew;
    }
}

430、难度中等:

方法一:原创:深度搜索(递归):用时超越100,内存变化不定

思路:
遍历题传链表(不考虑子链表),遍历时若当前节点存在子链表,那就调用递归深度搜索方法。

递归方法中我们所做的如下:
子链表中的节点可能还存在子链表,在判断当前节点是否存在子链表时,为防止出现死循环,我们还需新增一个变量用于表示当前节点的子链表正准备被循环、
我们需要注意节点间的关系,否则会leetcode会提示报错(后台会遍历答案链表):当前节点存在子链表时:
那么当前节点的next指向子链表的第一个节点,子链表第一个节点的prev指向当前节点。
当前节点原先的next的prev指向子链表的最后一个节点,子链表最后一个节点的next指向前节点原先的next
当前节点的子链表指向应修改为空

递归设计原理:把共性相同的流程提取出来

/*
// Definition for a Node.
class Node {
    public int val;
    public Node prev;
    public Node next;
    public Node child;
};
*/

class Solution {
    Node visited; // 保存正在被遍历子链表的节点

    public Node flatten(Node head) {
        if(head == null){
            return null;
        }
        // 答案链表的首节点作为返回值
        Node ans = head;
        // 不考虑子链表的情况下循环题传链表
        while(head != null){
            // 若当前节点有子链表,调用递归方法
            if(head.child != null){
                // 表示当前节点head的子链表正在被遍历
                visited = head;
                dfs(head);
            }
          
            head = head.next;
        }
        return ans;
    }
	
    // 递归方法
    public void dfs(Node head){
        // 保存当前节点的next防止丢失
        Node next = head.next;
        // 当前.next = 子链表首节点
        head.next = head.child;
        // 子链表首节点.prev = 当前
        head.child.prev = head;
        // 当前.child = null
        head.child = null;
        // 开始前往子链表的末尾
        while(head.next != null){
            // 若子链表的节点中也存在子链表。
            // 由于下面的dfs调用head,而这里的判断用的也是 head
            // 为防止陷入死循环不断进入该 if 判断语句调用 dfs(head);
            // 我们用visited保存head
            if(head.child != null && visited != head){
                visited = head;
                dfs(head);
            }
            head = head.next;
        }
        // 当前head是子链表中的最后一个节点,其next指向当前节点原先的next
        head.next = next;
        // 为防止当前节点是最后一个节点,next是null,但null无prev,为防止报错我们需加判断
        if(next != null){
            next.prev = head;
        }
        // 更新当前节点
        head = head.next;
    }
}

标签:Node,head,null,leetcode,链表,next,day035,138,节点
来源: https://blog.csdn.net/m0_52937388/article/details/121061713