【链表】算法例题

目录

[八、 链表](#八、 链表)

[57. 环形链表 ① ×](#57. 环形链表 ① ×)

[58. 两数相加 ② √](#58. 两数相加 ② √)

[59. 合并两个有序链表 ① √-](#59. 合并两个有序链表 ① √-)

[60. 随机链表的复制 ②](#60. 随机链表的复制 ②)

[61. 反转链表II ② ×](#61. 反转链表II ② ×)

[62. K个一组翻转链表 ③](#62. K个一组翻转链表 ③)

[63. 删除链表的倒数第N个结点 ② √-](#63. 删除链表的倒数第N个结点 ② √-)

[64. 删除排序链表中的重复元素II ② √-](#64. 删除排序链表中的重复元素II ② √-)

[65. 旋转链表 ② √-](#65. 旋转链表 ② √-)

[66. 分隔链表 ② √](#66. 分隔链表 ② √)

[67. LRU缓存 ②](#67. LRU缓存 ②)


八、 链表

57. 环形链表 ① ×

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

示例 1:

复制代码
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

复制代码
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

复制代码
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 104]
  • -105 <= Node.val <= 105
  • pos-1 或者链表中的一个 有效索引

进阶: 你能用 O(1)(即,常量)内存解决此问题吗?

力扣解析:

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

方法2:(0ms)

java 复制代码
    public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) return false;
        int n = 10010;
        while (n -- > 0) {
            head = head.next;
            if (head == null) return false;
        }
        return true;
    }

58. 两数相加 ② √

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:

复制代码
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2:

复制代码
输入:l1 = [0], l2 = [0]
输出:[0]

示例 3:

复制代码
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

提示:

  • 每个链表中的节点数在范围 [1, 100]
  • 0 <= Node.val <= 9
  • 题目数据保证列表表示的数字不含前导零

方法1:(1ms)

java 复制代码
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode head = new ListNode();
        ListNode temp = head;
        int more = 0;
        int res = 0;
        while (!(l1 == null && l2 == null)){
            res = (l1 != null? l1.val : 0) + (l2 != null? l2.val : 0) + more;
            if (res > 9){
                res = res - 10;
                more = 1;
            }else {
                more = 0;
            }
            ListNode newNode = new ListNode(res);
            temp.next = newNode;
            temp = temp.next;
            if (l1.next != null){
                l1 = l1.next;
            }
            if (l2.next != null){
                l2 = l2.next;
            }
        }
        if (more == 1){
            temp.next = new ListNode(more);
        }
        return head.next;
    }

59. 合并两个有序链表 ① √-

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:

复制代码
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

复制代码
输入:l1 = [], l2 = []
输出:[]

示例 3:

复制代码
输入:l1 = [], l2 = [0]
输出:[0]

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1l2 均按 非递减顺序 排列

方法1:(0ms)

java 复制代码
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if (list1 == null && list2 == null) {
            return null;
        } else if (list1 == null && list2 != null) {
            return list2;
        } else if (list1 != null && list2 == null) {
            return list1;
        }
        ListNode head = new ListNode();
        ListNode left = list1;
        ListNode right = list2;
        ListNode temp = head;
        while (left != null && right != null) {
            if (left.val < right.val) {
                temp.next = left;
                left = left.next;
            } else {
                temp.next = right;
                right = right.next;
            }
            temp = temp.next;
        }
        if (left == null) {
            temp.next = right;
        } else if (right == null) {
            temp.next = left;
        }
        return head.next;
    }

方法2:(0ms 递归)

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

60. 随机链表的复制 ②

61. 反转链表II ② ×

给你单链表的头指针 head 和两个整数 leftright ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表

示例 1:

复制代码
输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]

示例 2:

复制代码
输入:head = [5], left = 1, right = 1
输出:[5]

提示:

  • 链表中节点数目为 n
  • 1 <= n <= 500
  • -500 <= Node.val <= 500
  • 1 <= left <= right <= n

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

方法2:(0ms)

java 复制代码
    public ListNode reverseBetween(ListNode head, int m, int n) {
        // 定义一个dummyHead, 方便处理
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;

        // 初始化指针
        ListNode g = dummyHead;
        ListNode p = dummyHead.next;

        // 将指针移到相应的位置
        for(int step = 0; step < m - 1; step++) {
            g = g.next; p = p.next;
        }

        // 头插法插入节点
        for (int i = 0; i < n - m; i++) {
            ListNode removed = p.next;
            p.next = p.next.next;

            removed.next = g.next;
            g.next = removed;
        }

        return dummyHead.next;
    }

作者:贾卷积
链接:https://leetcode.cn/problems/reverse-linked-list-ii/solutions/138910/java-shuang-zhi-zhen-tou-cha-fa-by-mu-yi-cheng-zho/

62. K个一组翻转链表 ③

63. 删除链表的倒数第N个结点 ② √-

给你一个链表,删除链表的倒数第 n个结点,并且返回链表的头结点。

示例 1:

复制代码
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

复制代码
输入:head = [1], n = 1
输出:[]

示例 3:

复制代码
输入:head = [1,2], n = 1
输出:[1]

提示:

  • 链表中结点的数目为 sz
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

**进阶:**你能尝试使用一趟扫描实现吗?

方法1:(0ms)

java 复制代码
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if (head.next == null && n == 1){
            return null;
        }
        int count = 1;
        ListNode temp = head;
        while (temp.next != null){
            count++;
            temp = temp.next;
        }
        if (n == count){
            return head.next;
        }else {
            int cnt = 1;
            temp = head;
            while (cnt < count - n) {
                cnt++;
                temp = temp.next;
            }
            temp.next = temp.next.next;
            return head;
        }
    }

方法2:(0ms)

解题思路:

整体思路是让前面的指针先移动 n 步,之后前后指针共同移动直到前面的指针到尾部为止

首先设立预先指针 pre

设预先指针 pre 的下一个节点指向 head,设前指针为 start,后指针为 end,二者都等于 pre

start 先向前移动n步

之后 start 和 end 共同向前移动,此时二者的距离为 n,当 start 到尾部时,end 的位置恰好为倒数第 n 个节点

因为要删除该节点,所以要移动到该节点的前一个才能删除,所以循环结束条件为 start.next != null

删除后返回 pre.next,为什么不直接返回 head 呢,因为 head 有可能是被删掉的点

时间复杂度:O(n)

作者:画手大鹏

链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/solutions/7803/hua-jie-suan-fa-19-shan-chu-lian-biao-de-dao-shu-d/

java 复制代码
    //通过快慢指针来解决,类似于你要删除中间元素的题
    public ListNode removeNthFromEnd(ListNode head, int n) {
        //定义一个伪节点,用于返回结果
        ListNode dumpy = new ListNode(0);
        dumpy.next = head;
        //定义一个快指针,指向伪节点,用于遍历链表
        ListNode prev = dumpy;
        //定一个慢指针,
        ListNode tail = dumpy;
        //让快指针先移动 n 步
        while(n >0){
            prev = prev.next;
            n--;
        }
        //只要快指针不指向空,就继续循环
        while(prev.next !=null){
            //让快慢指针同时移动
            tail = tail.next;
            prev = prev.next;
        }
        //这时慢指针移动到的位置就是,要删除节点的前一个节点
        //所以只要删除当前节点的下一个节点
        tail.next = tail.next.next;
        //返回为指针指向的头节点
        return dumpy.next;
    }

64. 删除排序链表中的重复元素II ② √-

给定一个已排序的链表的头 head删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表

示例 1:

复制代码
输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]

示例 2:

复制代码
输入:head = [1,1,1,2,3]
输出:[2,3]

提示:

  • 链表中节点数目在范围 [0, 300]
  • -100 <= Node.val <= 100
  • 题目数据保证链表已经按升序 排列

方法1:(0ms)

java 复制代码
    public static ListNode deleteDuplicates(ListNode head) {
        if (head == null || head.next == null){
            return head;
        }
        ListNode pre = new ListNode(0);
        pre.next = head;
        ListNode newNode = pre;
        while (head != null && head.next != null){
            if (head.val != head.next.val) {   // 用值做比较,而不是两个节点做比较,节点一定不相等
                head = head.next;
                pre = pre.next;
            }else {
                while (head.next != null && head.next.val == head.val){  // 用值做比较
                    head = head.next;
                }
                head = head.next;
                pre.next = head;
            }
        }
        return newNode.next;
    }

解题思路:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

65. 旋转链表 ② √-

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k个位置。

示例 1:

复制代码
输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]

示例 2:

复制代码
输入:head = [0,1,2], k = 4
输出:[2,0,1]

提示:

  • 链表中节点的数目在范围 [0, 500]
  • -100 <= Node.val <= 100
  • 0 <= k <= 2 * 109

方法1:(1842ms)

java 复制代码
    public static ListNode rotateRight(ListNode head, int k) {
        if (head == null || head.next == null || k == 0){
            return head;
        }
        ListNode temp = head;
        ListNode pre = new ListNode(0);
        pre.next = head;
        int cnt = 0;
        while (cnt < k - 1){
            if (temp.next == null){
                temp = head;
            }else {
                temp = temp.next;
            }
            cnt++;
        }
        while (temp != null){
            pre = pre.next;
            temp = temp.next;
        }
        ListNode cur = pre;
        while (pre.next != null){
            pre = pre.next;
        }
        while (head != cur){
            pre.next = new ListNode(head.val);
            pre = pre.next;
            head = head.next;
        }
        return cur;
    }

方法2:(0ms)

java 复制代码
    public static ListNode rotateRight(ListNode head, int k) {
        if(head == null || head.next == null || k <= 0) {
            return head;
        }
        int n = 0;
        ListNode p = head;
        while(p != null) {
            p = p.next;
            n++;
        }
        k = k%n;
        if(k == n || k == 0) {
            return head;
        }
        ListNode slow = head , fast = head;
        for(int i=0; i<n-1; i++) {
            if(i >= k) {
                slow = slow.next;
            }
            fast = fast.next;
        }

        ListNode ans = head;
        ans = slow.next; //slow为新链表头结点的前一个节点 
        slow.next = null; //在旧链表中将新链表头结点与之前的节点斩断
        fast.next = head;  //旧链表的尾节点连接上旧链表的头结点
        return ans;
    }

66. 分隔链表 ② √

给你一个链表的头节点 head 和一个特定值x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你应当 保留 两个分区中每个节点的初始相对位置。

示例 1:

复制代码
输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]

示例 2:

复制代码
输入:head = [2,1], x = 2
输出:[1,2]

提示:

  • 链表中节点的数目在范围 [0, 200]
  • -100 <= Node.val <= 100
  • -200 <= x <= 200

方法1:(0ms)

java 复制代码
    public static ListNode partition(ListNode head, int x) {
        if (head == null || head.next == null){
            return head;
        }
        ListNode pre = new ListNode(0);
        pre.next = head;
        ListNode temp = head;
        ListNode newList = pre;
        while (temp != null && temp.val < x){
            temp = temp.next;
            pre = pre.next;
        }
        if (temp == null){
            return head;
        }
        ListNode cur = temp;
        ListNode newPre = new ListNode(0);
        newPre.next = temp;
        ListNode newCur = cur;
        while (cur != null){
            if (cur.val < x){
                newCur = cur.next;
                pre.next = new ListNode(cur.val);
                pre = pre.next;
                cur = newCur;
                newPre.next = cur;
            }else {
                cur = cur.next;
                newPre = newPre.next;
            }
        }
        pre.next = temp;
        return newList.next;
    }

方法2:(0ms):力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

java 复制代码
    public ListNode partition(ListNode head, int x) {
        ListNode smlDummy = new ListNode(0), bigDummy = new ListNode(0);
        ListNode sml = smlDummy, big = bigDummy;
        while (head != null) {
            if (head.val < x) {
                sml.next = head;
                sml = sml.next;
            } else {
                big.next = head;
                big = big.next;
            }
            head = head.next;
        }
        sml.next = bigDummy.next;
        big.next = null;
        return smlDummy.next;
    }

作者:Krahets
链接:https://leetcode.cn/problems/partition-list/solutions/2362068/86-fen-ge-lian-biao-shuang-zhi-zhen-qing-hha7/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

67. LRU缓存 ②

相关推荐
冰西瓜6001 分钟前
STL——vector
数据结构·c++·算法
天呐草莓3 分钟前
集成学习 (ensemble learning)
人工智能·python·深度学习·算法·机器学习·数据挖掘·集成学习
努力学算法的蒟蒻3 分钟前
day45(12.26)——leetcode面试经典150
算法·leetcode·面试
闻缺陷则喜何志丹13 分钟前
【离线查询 前缀和 二分查找 栈】P12271 [蓝桥杯 2024 国 Python B] 括号与字母|普及+
c++·算法·前缀和·蓝桥杯·二分查找··离线查询
Bdygsl16 分钟前
数据结构 —— 双向循环链表
数据结构·链表
程序员阿鹏21 分钟前
怎么理解削峰填谷?
java·开发语言·数据结构·spring·zookeeper·rabbitmq·rab
夏幻灵42 分钟前
为什么要配置环境变量?
笔记·算法
铭哥的编程日记1 小时前
Manacher算法解决所有回文串问题 (覆盖所有题型)
算法
LYFlied1 小时前
【每日算法】LeetCode 300. 最长递增子序列
前端·数据结构·算法·leetcode·职场和发展
ohnoooo91 小时前
251225 算法2 期末练习
算法·动态规划·图论