17.<tag-链表和基础常规题>-剑指 Offer 35. 复杂链表的复制 + 返回倒数第 k 个节点 + 剑指 Offer 18. 删除链表的节点 + lt.237. 删除链表中的节点
剑指 Offer 35. 复杂链表的复制
等用于lt.138. 复制带随机指针的链表
[案例需求]

[思路分析一, 回溯和哈希表]
- 借助哈希表建立起旧结点和新节点的映射关系, 把旧结点作为key, 知道旧结点我们就能得到新节点,
- 这样的话, 通过遍历旧结点, 我们可以得到新节点的各种信息. 无论是next域, 还是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) {
        //遍历链表, 把结点放入map
        // <旧结点, 新节点>
        // map.get(旧结点).xx = 旧结点.xx
        // 新链表如何连接的?
        Map<Node, Node> map = new HashMap<>();
        //1. 遍历原来的链表, 建立与对应的新链表结点的映射关系
        Node temp = head;
        while(temp != null){
            map.put(temp, new Node(temp.val));
            temp = temp.next;
        }
        temp = head;
        //2. 复制旧结点的内容给新节点
        while(temp != null){
            map.get(temp).next = map.get(temp.next); //新的结点的next还得是新的结点哦!!
            map.get(temp).random = map.get(temp.random);
            temp = temp.next;
        }
        return map.get(head);
    }
}
时空复杂度:
[思路分析二, 迭代和结点拆分]
- 待补充
面试题 02.02. 返回倒数第 k 个节点
[案例需求]

[思路分析一, 栈]
- 本题解法多种多样,
 -解法一, 先遍历一次链表, 得到链表中结点的总个数, 然后再遍历 n - k 次, 即可找到倒数第k个结点, 返回这个节点值即可;
- 解法二, 先遍历一次链表, 把遍历到的结点放入到栈中, 然后出栈 k次即可得到倒数第k个结点, 返回这个结点值即可;
[代码实现]
// 1. 栈
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int kthToLast(ListNode head, int k) {
        //栈
        Stack<ListNode> stack = new Stack<>();
        ListNode temp = head;
        while(temp != null){
            stack.push(temp);
            temp = temp.next;
        }
        int count = stack.size();
        while(k > 1){
            stack.pop();
            k--;
        }
        return stack.pop().val;
    }
}
[思路分析二, 快慢针]
- 对于这种求倒数结点/ 返回链表中间结点的题目, 都可以使用快慢针
- 让slow和 fast指针保持一定的距离, 距离的大小是根据题目而定的, 而且我们也需要提前移动快指针
剑指 Offer 18. 删除链表的节点
[案例需求]

[思路分析]
- 对于删除链表中的指定节点, 记住两点就够了; 
  - 最好要有虚节点 dummyNode, 因为能够让链表的首节点和链表中的其他节点的删除操作是保持一致的;
- 删除一个结点, 我们必须得知道他的前驱结点 !! 所以我们在写代码时, 都是倾向于是 temp.next.val == val, 而不是temp.val == val
 
[代码实现]
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
       ListNode dummyNode = new ListNode(-1);
       dummyNode.next = head;
       ListNode temp = dummyNode;
       while(temp.next != null){
           if(temp.next.val == val){
               temp.next = temp.next.next;
               break;
           }
           temp = temp.next;
       }
       return dummyNode.next;
    }
}
lt.237. 删除链表中的节点
[案例需求]

[思路分析]
-  很有代表性的一类问题解决方案: 当只知道链表中的待删除链表结点, 而我们无法访问head结点时, 如何删除这个结点? 
-  答曰: 偷梁换柱
  
[代码实现]
class Solution {
    public void deleteNode(ListNode node) {
        node.val = node.next.val;
        node.next = node.next.next;
    }
}
 
                    
                     
                    
                 
                    
                

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号