代码随想录算法训练营第四天 | LeetCode 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II

简介: 代码随想录算法训练营第四天 | LeetCode 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II

1. LeetCode 24. 两两交换链表中的节点

1.1 思路

  1. 定义虚拟头节点dummyhead,要不然每次针对头结点(没有前一个指针指向头结点),还要单独处理,并且cur=dummyhead,因为这里的步骤是首先cur下一个先指向节点2,然后节点2下一个指向节点1,再然后是节点1下一个指向节点3,最后让cur指向翻转后的节点1,直接cur=first就行。所以需要dummyhead的原因就是因为cur要指向要翻转的两个节点的前一个节点。
  2. 具体实现先定义dummyhead,并且让dummyhead的next指向head,cur指向dummyhead,这样指向才能让最开始操作头节点和第二个节点的翻转
  3. 遍历过程while(cur.next!=null&&cur.next.next!=null),第一个条件是针对如果链表上是偶数个节点,那如果cur.next为空那就结束循环,第二个条件是针对如果链表上是奇数个节点,那如果cur.next.next为空那就结束循环,这里不理解可以画图理解一下,注意这里要用&&不能用||,因为如果用了||那如果是奇数个节点,cur.next就不为空那就进入循环里进行翻转了,但奇数个节点最后单独那个节点不用翻转,所以要用&&
  4. 翻转步骤:首先cur下一个先指向节点2,然后节点2下一个指向节点1,再然后是节点1下一个指向节点3,最后让cur指向翻转后的节点1,直接cur指向节点1就行(因为翻转的是两个节点,到下两个节点了,直接后移两位,也就是指向节点1就行)。这里问题在于如果直接用cur.next=cur.next.next;cur.next.next=cur.next的方式操作的话就找不到原来的指针了,因此要用first保存cur.next,second保存cur.next.next,temp作为节点3保存cur.next.next.next(这里节点3有可能是个空指针,但没关系的)。上面翻转的cur.next=second;second.next=first;first.next=temp;cur=first;
  5. return dummyhead.next就是新的链表

1.2 代码

class Solution {
  public ListNode swapPairs(ListNode head) {
        ListNode dumyhead = new ListNode(-1); // 设置一个虚拟头结点
        dumyhead.next = head; // 将虚拟头结点指向head,这样方面后面做删除操作
        ListNode cur = dumyhead;
        ListNode temp; // 临时节点,保存两个节点后面的节点
        ListNode firstnode; // 临时节点,保存两个节点之中的第一个节点
        ListNode secondnode; // 临时节点,保存两个节点之中的第二个节点
        while (cur.next != null && cur.next.next != null) {
            temp = cur.next.next.next;
            firstnode = cur.next;
            secondnode = cur.next.next;
            cur.next = secondnode;       // 步骤一
            secondnode.next = firstnode; // 步骤二
            firstnode.next = temp;      // 步骤三
            cur = firstnode; // cur移动,准备下一轮交换
        }
        return dumyhead.next;  
    }
}

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

2.1 思路

  1. 如何找到倒数第n个节点呢?定义虚拟头节点dummyhead,它的next指向head,好处依然是方便我们不需要判断要删除的节点是不是头节点,这样删除的操作比较统一
  2. 定义fast和slow指针都指向dummyhead,然后让fast指针先移动n+1步,然后fast和slow同时移动直到fast指向null,此时slow就指向要删除的节点的前一个节点了

2.2 代码

public ListNode removeNthFromEnd(ListNode head, int n){
    ListNode dummyNode = new ListNode(0);
    dummyNode.next = head;
    ListNode fastIndex = dummyNode;
    ListNode slowIndex = dummyNode;
    //只要快慢指针相差 n 个结点即可
    for (int i = 0; i < n  ; i++){
        fastIndex = fastIndex.next;
    }
    while (fastIndex.next != null){
        fastIndex = fastIndex.next;
        slowIndex = slowIndex.next;
    }
    //此时 slowIndex 的位置就是待删除元素的前一个位置。
    //具体情况可自己画一个链表长度为 3 的图来模拟代码来理解
    slowIndex.next = slowIndex.next.next;
    return dummyNode.next;
}

3. LeetCode 面试题 02.07. 链表相交

3.1 思路

  1. 让curLong指向headA,curShort指向headB,假设是这样
  2. 然后遍历求出两链表的长度,以及差值len,让长的先走差值len步
  3. 然后while(curLong!=curShort)一直遍历两个后移,直到相等,最后return 其中一个就可以

3.2 代码

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA==null||headB==null){
            return null;
        }
        //1、分别求两个链表的长度
        int len1=0;
        int len2=0;
        ListNode curLong=headA;//假设长的是链表A
        ListNode curShort=headB;//假设短的是链表B
        while(curLong!=null){
            len1++;
            curLong=curLong.next;
        }
        while(curShort!=null){
            len2++;
            curShort=curShort.next;
        }
        //2、求差值步的len
        int len=len1-len2;
        if(len<0){//差值为负,说明长的是B
            curLong=headB;
            curShort=headA;
            len=len2-len1;
        }else{//差值为正,说明长的就是A
            curLong=headA;
            curShort=headB;
        }
        //3、长链表先走len步,这是个差值步,走了之后再一起走
        while(len>0){
            curLong=curLong.next;
            len--;
        }
        //4、一起走,直到相遇
        while(curLong!=curShort){
            curLong=curLong.next;
            curShort=curShort.next;
        }
        return curLong;//return curShort也行
    }
}

4. LeetCode 142.环形链表II

4.1 思路

  1. 这题其实有两问,一问判断是否有环,一问是找到环的入口
  2. 判断是否有环:快慢指针,相遇说明有环。怎么想到呢?假设没环是一条直线,这时快慢指针不可能相遇,这时想为什么快慢指针就会相遇呢?就不会一直错过吗?快指针每次走两步,慢指针每次走一步,肯定是快指针先入环然后才是慢指针,然后入环后就相当于快指针在追慢指针,然后速度差为1,那么快指针就相对于慢指针移动来说每次就移动一个位置,就相当于在一个节点一个节点的在靠近慢指针,所以一定会在环里相遇
  3. 如何找环入口:假设起点到环的入口距离为x,环入口到相遇点的距离为y,相遇点到环入口的距离为z。在相遇点相遇慢指针走了x+y,快指针走了x+y+n(y+z),因为快指针走了n(n>=1)圈,因为快指针速度是慢指针的2倍,因此等式2(x+y)=x+y+n(y+z),解得x=n(y+z)-y,如果n=1,则x=z

4.2 代码

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {// 有环
                ListNode index1 = fast;
                ListNode index2 = head;
                // 两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口
                while (index1 != index2) {
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }
        }
        return null;
    }
}
相关文章
|
4月前
|
机器学习/深度学习 算法
24. 两两交换链表中的节点, 19.删除链表的倒数第N个节点 ,面试题 02.07. 链表相交
1. **两两交换链表中的节点**:通过引入虚拟头结点,使所有节点都能采用统一的交换逻辑,避免对头结点单独处理。 2. **删除链表的倒数第N个节点**:利用双指针技巧,让快慢指针保持N个节点的距离,当快指针到达末尾时,慢指针正好指向待删除节点的前一个节点。 3. **链表相交**:先计算两链表长度并调整起点,确保从相同距离末尾的位置开始遍历,从而高效找到相交节点或确定无交点。 以上方法均在时间复杂度和空间复杂度上进行了优化,适合用于理解和掌握链表的基本操作及常见算法设计思路。
|
4月前
|
人工智能 前端开发 Java
Java 面试资料中相关代码使用方法与组件封装方法解析
这是一份详尽的Java面试资料代码指南,涵盖使用方法与组件封装技巧。内容包括环境准备(JDK 8+、Maven/Gradle)、核心类示例(问题管理、学习进度跟踪)、Web应用部署(Spring Boot、前端框架)、单元测试及API封装。通过问题库管理、数据访问组件、学习进度服务和REST接口等模块化设计,帮助开发者高效组织与复用功能,同时支持扩展如用户认证、AI推荐等功能。适用于Java核心技术学习与面试备考,提升编程与设计能力。资源链接:[点此下载](https://pan.quark.cn/s/14fcf913bae6)。
105 6
Java 面试资料中相关代码使用方法与组件封装方法解析
|
7月前
|
算法
面试场景题:如何设计一个抢红包随机算法
本文详细解析了抢红包随机算法的设计与实现,涵盖三种解法:随机分配法、二倍均值法和线段切割法。随机分配法通过逐次随机分配金额确保总额不变,但易导致两极分化;二倍均值法优化了金额分布,使每次抢到的金额更均衡;线段切割法则将总金额视为线段,通过随机切割点生成子金额,手气最佳金额可能更高。代码示例清晰,结果对比直观,为面试中类似算法题提供了全面思路。
1222 16
|
9月前
|
算法 安全 Java
Java线程调度揭秘:从算法到策略,让你面试稳赢!
在社招面试中,关于线程调度和同步的相关问题常常让人感到棘手。今天,我们将深入解析Java中的线程调度算法、调度策略,探讨线程调度器、时间分片的工作原理,并带你了解常见的线程同步方法。让我们一起破解这些面试难题,提升你的Java并发编程技能!
283 16
|
12月前
|
算法 Java 数据库
美团面试:百亿级分片,如何设计基因算法?
40岁老架构师尼恩分享分库分表的基因算法设计,涵盖分片键选择、水平拆分策略及基因法优化查询效率等内容,助力面试者应对大厂技术面试,提高架构设计能力。
美团面试:百亿级分片,如何设计基因算法?
|
12月前
|
机器学习/深度学习 算法 Java
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
|
12月前
|
算法 Java 数据库
美团面试:百亿级分片,如何设计基因算法?
40岁老架构师尼恩在读者群中分享了关于分库分表的基因算法设计,旨在帮助大家应对一线互联网企业的面试题。文章详细介绍了分库分表的背景、分片键的设计目标和建议,以及基因法的具体应用和优缺点。通过系统化的梳理,帮助读者提升架构、设计和开发水平,顺利通过面试。
美团面试:百亿级分片,如何设计基因算法?
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
190 6
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
142 6
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
308 2

热门文章

最新文章