当前位置: 首页 > news >正文

【力扣刷题】Day04——链表专题

文章目录

    • 4. 两两交换链表中的节点
    • 5. 删除链表的倒数第N个节点
    • 6. 链表相交
    • 7. 环形链表II


上一篇文章:【力扣刷题】Day03——链表专题_塔塔开!!!的博客-CSDN博客

4. 两两交换链表中的节点

题目链接:24. 两两交换链表中的节点 - 力扣(LeetCode)

思路:由于是一对一对的进行交换,因此我们枚举的时候也要一对一对的来。

尤其要注意

  • 交换的顺序
  • 头节点也发生了修改,因此加入虚拟头节点

在这里插入图片描述

Code

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;

        // 一对一对的枚举
        for(ListNode p = dummy; p.next != null && p.next.next != null;){
            ListNode a = p.next;
            ListNode b = a.next;

            // 交换
            p.next = b;// 1
            a.next = b.next;// 2
            b.next = a;// 3
            // 指针移动,为下一次做好准备
            p = a;// 4
        }
        return dummy.next;
    }
}

5. 删除链表的倒数第N个节点

题目链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

要删除节点就要找到要删除节点的前驱在哪:

思路一:枚举找到要删除节点的前驱

在这里插入图片描述

当头节点会改变时我们引入虚拟头节点

Code

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
       
       ListNode dummy = new ListNode(-1);
       dummy.next = head;

       int len = 0, cnt = 0; 
       for(ListNode cur = dummy; cur != null; cur = cur.next) len ++;
       for(ListNode cur = dummy; cur != null; cur = cur.next){
           cnt ++;
           if(len - n == cnt){// 找到倒数第n+1得位置
               cur.next = cur.next.next;
               break; 
           }
       }
       return dummy.next;

    }
}

思路二:双指针优化:双指针找到要删除节点的前驱

上述得关键是要找到删除节点得前一个节点,采用的是遍历的方式,现在我们用双指针进行优化:

1、创建虚拟结点dummydummy指向head.next
2、指针firstsecond初始化均指向dummyfirst指针走n步,first,second指针同时向后走,直到first走到末尾时终止
3、这样找到了删除结点的前一个结点,让前一个结点指向删除结点的后一个结点即可

在这里插入图片描述

Code

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;

        ListNode first = dummy;
        ListNode second = dummy;

        for(int i = 0; i < n; i ++) first = first.next;// first先往后走n步
        // 注意:这里是first.next != null 而不是first
        // 因为当指针走到最后一个节点了,first它不为空,还会再执行一次循环体,这样就不对了
        while(first.next != null){
            first = first.next;
            second = second.next;
        }
        second.next = second.next.next;

        return dummy.next;
    }
}

6. 链表相交

题目链接:160. 相交链表 - 力扣(LeetCode)

一种非常巧妙得思路,分别给链表A和链表B设置指针A和指针B,然后开始遍历链表,如果当前链表走完了,则将指针指向另一个链表得头部继续遍历,直到两个指针相遇。

两个指针走过得路径长度分别为:

  • 指针A:a + b + c
  • 指针B:a + b + c
  • 明显它们走过得路径长度是一样的,即相遇的节点

两个链表不相交:
在这里插入图片描述

a,b 分别代表两个链表的长度,则两个指针分别走 a+b 步后都变成 null,最终null相等

两个链表相交:

在这里插入图片描述

则两个指针分别走 a+b+c 步后在两链表交汇处相遇。

Code

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode p = headA;
        ListNode q = headB;

        while(p != q){
            if(p == null) p = headB;
            else p = p.next;

            if(q == null) q = headA;
            else q = q.next;
        }
        return p;
    }
}

7. 环形链表II

题目链接:142. 环形链表 II - 力扣(LeetCode)

解法一:哈希判重,入口即为第一次重复的节点

空间复杂度:O(n)

Code

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        Set<ListNode> set = new HashSet<ListNode>();
        for(ListNode p = head; p != null; p = p.next){
            if(set.contains(p)) return p;
            set.add(p);
        }
        return null;
    }
}

解法二:快慢指针

空间复杂度:O(1)

思路:使用快慢指针,快指针每次走两步,满指针每次走一步,当两个指针第一次相遇的时候,将其中一个指针(快)回到起点,然后快慢指针一起每次走一步,再次相遇即为环的入口。
在这里插入图片描述

假设C点为两个指针的相遇点

第一次相遇:

  • 慢指针:x + y
  • 快指针:x + y + n(y + z),快指针可能转了好几圈

不妨直接设快指针转了1圈,由2倍得出等式:

2(x + y) = x + y + (y + z)

化简得:x = z

而此时又是在第一次相遇的点(C),将其中一个指针(快)回到起点,然后快慢指针一起每次走一步,再次相遇即为环的入口(距离都是一样的,都为z)。

详细解答证明:把环形链表讲清楚! 如何判断环形链表?如何找到环形链表的入口? LeetCode:142.环形链表II_哔哩哔哩_bilibili

Code

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;

        // 快慢指针用起点开始,快走两步,慢走一步
        // 当快慢第一次相遇时,快回到起点,然后快慢一起走一步,再次相遇时即为环的入口
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;

            if(slow == fast){// 第一次相遇
                fast = head;
                while(fast != slow){
                    fast = fast.next;
                    slow = slow.next;
                }
                // 第二次相遇即为环的入口
                return fast;
            }
        }    
        return null;
    }
}

相关文章:

  • 云计算以及云计算安全相关的中文概述
  • 【 C++ 】哈希表底层结构剖析
  • Swift 基础语法 - 数据类型
  • js单行代码------对象
  • T1061 求整数的和与均值(信息学一本通C++)
  • Java注解-最通俗易懂的讲解
  • 特殊类设计
  • 【STL***deque容器二】
  • 多测师肖sir_高级讲师_第2个月第8讲解类
  • 各编程语言 + aardio 相互调用示例
  • SpringCloud概述
  • element的Form表单就应该这样用
  • Linux基础组件之死锁检测
  • TypeScript——函数(函数定义类型、可选参数和默认参数、剩余参数、函数类型变量、使用接口封装函数变量类型)
  • T1056点和正方形的关系 (信息学一本通C++)
  • IE9 : DOM Exception: INVALID_CHARACTER_ERR (5)
  • 【翻译】babel对TC39装饰器草案的实现
  • ES6核心特性
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • 基于遗传算法的优化问题求解
  • 前端 CSS : 5# 纯 CSS 实现24小时超市
  • 想写好前端,先练好内功
  • 用Node EJS写一个爬虫脚本每天定时给心爱的她发一封暖心邮件
  • 智能合约开发环境搭建及Hello World合约
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • elasticsearch-head插件安装
  • 数据库巡检项
  • ​queue --- 一个同步的队列类​
  • ​Z时代时尚SUV新宠:起亚赛图斯值不值得年轻人买?
  • #Z2294. 打印树的直径
  • (1)(1.13) SiK无线电高级配置(六)
  • (1)Nginx简介和安装教程
  • (10)ATF MMU转换表
  • (3)(3.2) MAVLink2数据包签名(安全)
  • (Repost) Getting Genode with TrustZone on the i.MX
  • (附源码)ssm基于微信小程序的疫苗管理系统 毕业设计 092354
  • (附源码)ssm学生管理系统 毕业设计 141543
  • (学习日记)2024.01.09
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (原創) 是否该学PetShop将Model和BLL分开? (.NET) (N-Tier) (PetShop) (OO)
  • (转)c++ std::pair 与 std::make
  • (转)拼包函数及网络封包的异常处理(含代码)
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • (转载)虚函数剖析
  • .NET基础篇——反射的奥妙
  • .NET精简框架的“无法找到资源程序集”异常释疑
  • @RequestMapping处理请求异常
  • [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序...
  • [\u4e00-\u9fa5] //匹配中文字符
  • [22]. 括号生成
  • [BUUCTF]-Reverse:reverse3解析
  • [bzoj4240] 有趣的家庭菜园
  • [C++]Leetcode17电话号码的字母组合
  • [DAX] MAX函数 | MAXX函数
  • [hdu 1247]Hat’s Words [Trie 图]