Leetcode 刷题记录 08 —— 链表第二弹

本系列为笔者的 Leetcode 刷题记录,顺序为 Hot 100 题官方顺序,根据标签命名,记录笔者总结的做题思路,附部分代码解释和疑问解答,01~07为C++语言,08及以后为Java语言。

01 合并两个有序链表

java 复制代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        
    }
}

方法一:递归

java 复制代码
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if(list1 == null){
            return list2;
        }else if(list2 == null){
            return list1;
        }else if(list1.val < list2.val){
            list1.next = mergeTwoLists(list1.next, list2);
            return list1;
        }else{
            list2.next = mergeTwoLists(list1, list2.next);
            return list2;
        }
    }
}

方法二:遍历

java 复制代码
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode list3 = new ListNode(-1);

        ListNode flag = list3; //论标记的重要性
        while(list1 != null && list2 != null){
            if(list1.val < list2.val){
                list3.next = list1;
                list1 = list1.next;
            }else{
                list3.next = list2;
                list2 = list2.next;
            }
            list3 = list3.next;
        }

        if(list1 != null){
            list3.next = list1;
        }else{
            list3.next = list2;
        }
        
        return flag.next; //返回头结点指针
    }
}

02 两数相加

方法一:设新链表、进位值,不断创建新节点

java 复制代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode start = null, end = null;
        int flag = 0;
        while(l1 != null || l2 != null){
            //1. 计算单位加和
            int n1 = l1 != null ? l1.val : 0;
            int n2 = l2 != null ? l2.val : 0;
            int sum = n1 + n2 + flag;

            //2. 添加新链表结点,计算进位
            if(start == null){
                start = end = new ListNode(sum % 10);
            }else{
                end.next = new ListNode(sum % 10);
                end = end.next;
            }
            flag = sum / 10;

            //3. 移动结点
            if(l1 != null){
                l1 = l1.next;
            } 
            if(l2 != null){
                l2 = l2.next;
            } 
        }

        //4. 特殊情况:高位进一
        if(flag > 0){
            end.next = new ListNode(flag);
        }

        return start;
    }
}

03 删除链表的倒数第N个结点

java 复制代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0, head);
        ListNode first = head;
        ListNode second = dummy; //可移动结点

        //1. first移动n位
        for(int i=0; i<n; ++i){
            first = first.next;
        }

        //2. first、second同时移动,first=null为止
        while(first != null){
            first = first.next;
            second = second.next;
        }

        //3. 删除结点,释放空间
        second.next = second.next.next;
        
        return dummy.next;
    }
}

dummy.next不是head吗,那么ListNode ans = dummy.next; return ans;存在的意义是什么?

如果 n 等于链表的长度,即需要删除头节点时,单独使用 head 会导致很复杂的边界条件处理,在这种情况下,dummy 提供一个可靠的起始节点,方便统一处理删除节点的逻辑。

② 为什么要新建一个链表ListNode dummy = new ListNode(0, head);,而不是在原有链表上操作?

dummy节点简化了边界条件的处理,所有节点,无论是否是头节点,都可以以一致的方式处理,最终可以直接返回dummy.next作为结果,这样不会影响原始链表的头节点指向。

04 两两交换链表中的节点

java 复制代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode dummy = new ListNode(0, head);
        ListNode flag = dummy; //可移动结点

        while(flag.next != null && flag.next.next != null){
            //1. 顺次两个节点
            ListNode a = flag.next;
            ListNode b = flag.next.next;

            //2. 交换
            flag.next = b;
            a.next = b.next;
            b.next = a;
            flag = a;
        }

        return dummy.next; //万金油
    }
}

05 K个一组翻转链表

java 复制代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    //需要pre,head,tail,nex四个指针
    public ListNode reverseKGroup(ListNode head, int k) {
        //1.创建dummy链表,返回dummy.next
        ListNode dummy = new ListNode(0, head);
        ListNode pre = dummy;

        //2.寻找(head,tail),调用myReverse方法
        while(head != null){
            ListNode tail = pre;
            for(int i=0; i<k; ++i){
                tail = tail.next;
                if(tail == null){
                    return dummy.next;
                }
            }
            ListNode nex = tail.next;

            ListNode[] reverse = myReverse(head, tail);
            head = reverse[0];
            tail = reverse[1];

            //3.连接断裂链表
            pre.next = head;
            tail.next = nex;
            pre = tail;
            head = nex;
        }

        return dummy.next;
    }

    //需要a,b,c三个指针
    public ListNode[] myReverse(ListNode start, ListNode end){
        //1.寻找a,b,c
        //2.核心操作:a->c
        //3.临界条件:end==c
        ListNode a = start;
        ListNode c = end.next;

        while(end != c){
            ListNode b = a.next;
            a.next = c;
            c = a;
            a = b;
        }

        return new ListNode[]{end, start};
    }
}

06 随机链表的复制

失败代码

java 复制代码
/*
// 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) {
        //1.创建dummy链表,返回dummy.next
        Node dummy = new Node(0);
        dummy.next = head;

        //2.第一次遍历,复制结点值和next指针
        Node end = dummy;
        Node start = head;
        while(start != null){
            end.next = new Node(start.val); //复制结点值
            end = end.next;
            end.next = start.next; //复制next指针
            start = start.next;
        }


        //3.第二次遍历,复制random指针
        Node end2 = dummy;
        Node start2 = head;
        while(start2 != null){
            end2 = end2.next;
            end2.random = start2.random; //复制random指针
            start2 = start2.next;
        }

        return dummy.next;
    }
}

问题:当前复制的 random 指针仍然指向原链表中的节点,而不是新复制链表中的节点。

方法一:递归

java 复制代码
/*
// 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 {
    //1.创建映射
    Map<Node, Node> cachedNode = new HashMap<Node, Node>();

    public Node copyRandomList(Node head) {
        if(head == null){
            return null;
        }

        //2.创建新节点,拷贝val
        if(!cachedNode.containsKey(head)){
            Node headNew = new Node(head.val);
            cachedNode.put(head, headNew);

            //3.递归,拷贝next,random
            headNew.next = copyRandomList(head.next);
            headNew.random = copyRandomList(head.random);
        }

        return cachedNode.get(head);
    }
}

方法二:结点拆分

java 复制代码
/*
// 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;
        }

        //1. 一次遍历:节点插入,拷贝val
        for(Node node = head; node != null; node = node.next.next){
            Node nodeNew = new Node(node.val);
            nodeNew.next = node.next;
            node.next = nodeNew;
        }

        //2. 二次遍历:拷贝random
        for(Node node = head; node != null; node = node.next.next){
            Node nodeNew = node.next;
            nodeNew.random = (node.random != null) ? node.random.next : null;
        }

        //3. 三次遍历:拷贝next
        Node headNew = head.next;
        for(Node node = head; node != null; node = node.next){
            Node nodeNew = node.next;
            node.next = node.next.next;
            nodeNew.next = (node.next != null) ? nodeNew.next.next : null;
        }

        return headNew;
    }
}

07 排序链表

方法一:快慢指针归并排序 + 合并有序链表

java 复制代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
        return sortList(head, null); //没说区间,默认右端点为null 

    }

    //1.伪二分(快慢指针)
    public ListNode sortList(ListNode head, ListNode tail){
        // if(head == null || head.next == tail){ //特殊情况判断
        //     return head;
        // }

        if (head == null) {
            return head;
        }
        if (head.next == tail) {
            head.next = null;
            return head;
        }

        ListNode fast = head;
        ListNode slow = head;
        while(fast != tail){
            fast = fast.next;
            slow = slow.next;
            if(fast != tail){
                fast = fast.next;
            }
        }

        //2.归并排序(head,tail)
        ListNode mid = slow;
        ListNode list1 = sortList(head, mid);
        ListNode list2 = sortList(mid, tail);
        ListNode sorted = mergeList(list1, list2);
        return sorted;
    }

    //3.合并两个有序链表
    public ListNode mergeList(ListNode l1, ListNode l2){
        ListNode l3 = new ListNode(0);

        ListNode flag = l3; //return flag.next;
        while(l1 != null && l2 != null){
            if(l1.val < l2.val){
                l3.next = l1;
                l1 = l1.next;
            }else{
                l3.next = l2;
                l2 = l2.next;
            }
            l3 = l3.next;
        }

        if(l1 != null){
            l3.next = l1;
        }
        if(l2 != null){
            l3.next = l2;
        }

        return flag.next;
    }
}

方法二:倍增长度 + 合并两个有序链表

java 复制代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
        if(head == null){ //特殊情况判断
            return head;
        }

        //1.获取链表长度length
        int length = 0;
        ListNode node = head;
        while(node != null){
            length++;
            node = node.next;
        }

        //2.倍增长度subLength
            //a.四指针 pre/head1/head2/nex
            //b.两断链 
            //c.一接骨
        ListNode dummy = new ListNode(0, head);
        for(int subLength = 1; subLength < length; subLength <<= 1){
            ListNode pre = dummy;
            ListNode curr = dummy.next;
            while(curr != null){
                ListNode head1 = curr;
                for(int i=1; i<length && curr.next != null; i++){
                    curr = curr.next;
                }

                ListNode head2 = curr;
                curr.next = null;
                curr = head2;
                for(int i=1; i<length && curr.next != null && curr != null; i++){
                    curr = curr.next;
                }

                ListNode nex = null;
                if(curr.next != null){
                    nex = curr.next;
                    curr.next = null;
                }

                ListNode merged = mergeList(head1, head2);
                pre.next = merged;
                pre = curr;
                curr = nex;
            }
        }
        //3.合并方法

        return dummy.next;
    }

    //合并两个有序链表
    public ListNode mergeList(ListNode l1, ListNode l2){
        ListNode l3 = new ListNode(0);

        ListNode flag = l3; //return flag.next;
        while(l1 != null && l2 != null){
            if(l1.val < l2.val){
                l3.next = l1;
                l1 = l1.next;
            }else{
                l3.next = l2;
                l2 = l2.next;
            }
            l3 = l3.next;
        }

        if(l1 != null){
            l3.next = l1;
        }
        if(l2 != null){
            l3.next = l2;
        }

        return flag.next;
    }
}

.next;

}

复制代码
//合并两个有序链表
public ListNode mergeList(ListNode l1, ListNode l2){
    ListNode l3 = new ListNode(0);

    ListNode flag = l3; //return flag.next;
    while(l1 != null && l2 != null){
        if(l1.val < l2.val){
            l3.next = l1;
            l1 = l1.next;
        }else{
            l3.next = l2;
            l2 = l2.next;
        }
        l3 = l3.next;
    }

    if(l1 != null){
        l3.next = l1;
    }
    if(l2 != null){
        l3.next = l2;
    }

    return flag.next;
}

}

复制代码
相关推荐
Han.miracle20 小时前
数据结构——二叉树的从前序与中序遍历序列构造二叉树
java·数据结构·学习·算法·leetcode
Le1Yu21 小时前
分布式事务以及Seata(XA、AT模式)
java
寒山李白21 小时前
关于Java项目构建/配置工具方式(Gradle-Groovy、Gradle-Kotlin、Maven)的区别于选择
java·kotlin·gradle·maven
mit6.82421 小时前
前后缀分解
算法
独自破碎E1 天前
判断链表是否为回文
数据结构·链表
你好,我叫C小白1 天前
C语言 循环结构(1)
c语言·开发语言·算法·while·do...while
无妄无望1 天前
docker学习(4)容器的生命周期与资源控制
java·学习·docker
MC丶科1 天前
【SpringBoot 快速上手实战系列】5 分钟用 Spring Boot 搭建一个用户管理系统(含前后端分离)!新手也能一次跑通!
java·vue.js·spring boot·后端
千码君20161 天前
React Native:从react的解构看编程众多语言中的解构
java·javascript·python·react native·react.js·解包·解构
夜白宋1 天前
【word多文档docx合并】
java·word