【链表】算法例题

目录

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

[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缓存 ②

相关推荐
哭泣的眼泪40813 分钟前
解析粗糙度仪在工业制造及材料科学和建筑工程领域的重要性
python·算法·django·virtualenv·pygame
Microsoft Word1 小时前
c++基础语法
开发语言·c++·算法
天才在此1 小时前
汽车加油行驶问题-动态规划算法(已在洛谷AC)
算法·动态规划
莫叫石榴姐2 小时前
数据科学与SQL:组距分组分析 | 区间分布问题
大数据·人工智能·sql·深度学习·算法·机器学习·数据挖掘
茶猫_3 小时前
力扣面试题 - 25 二进制数转字符串
c语言·算法·leetcode·职场和发展
Hera_Yc.H4 小时前
数据结构之一:复杂度
数据结构
肥猪猪爸5 小时前
使用卡尔曼滤波器估计pybullet中的机器人位置
数据结构·人工智能·python·算法·机器人·卡尔曼滤波·pybullet
linux_carlos5 小时前
环形缓冲区
数据结构
readmancynn5 小时前
二分基本实现
数据结构·算法
萝卜兽编程5 小时前
优先级队列
c++·算法