【LeetCode题目详解】(二)206.反转链表、876.链表的中间结点

简介: 【LeetCode题目详解】(二)206.反转链表、876.链表的中间结点

一、力扣第206题:反转链表

题目链接:206. 反转链表 - 力扣(Leetcode)

题目描述:

1.思路一

对于这道题而言,我们一开始接触这种链表题可能都是懵的,但是不要慌,认真分析,一定可以做出来的。

我们就看它一开始给的测试用例来进行分析,如下图所示,他让这个链表经过一系列变化后反转了

我们仔细观察这些区别,我们发现其实只要我们将这个指针的指向给改变了就可以了。所以我们的重点在于如何改变指针的指向

我们要改变它的指向我们应该起码给出两个指针先来记录一下要改变的两个指针

如下图所示,我们使用n1和n2来记录两个地址,其中让n1为指针,n2为链表头

我们的第一步也很简单只需要让n2->next=n1,就可以了,但是与此同时,2所在的结点也就失去联系了,我们无法找到后面的结点,所以这种方法其实是不可行的。我们还需要第三个指针来记录后面的结点

如下图所示,我们在改变n2的指向之前先让n3=n2->next。也就是记录了后面的一个结点

然后接下来,我们让n1的值变为现在n2的值,让n2的值变为现在n3的值,让n3也向后走一步,这样就变成这样

然后我们不难想到,我们继续让n2->next=n1,也就是改变了2的指向关系

然后我们继续让三个指针都往后走

这样一来,我们继续让n2->next =n1

然后我们继续让三个指针移动

继续改变4的指向关系

继续向后移动,并改变指向,这时候,我们发现我们已经完成了我们的目标,成功反转了这个指针

这里一定要细心,要注意我们的循环过程是,先改变指向,在向后移动,这里很多人只关心到改变了指向,却没有将指针移动。然后就会导致一些错误,指针移动后如下图

既然我们现在反转了这个指针,那么我们现在来理清我们的思路,首先我们定义了三个指针,然后我们使用了一个循环,这个循环中先改变指针的指向,然后让三个指针依次往下走。当n2为空指针的时候,循环也就结束了

然后我们使用了一个循环的过程. 最后我们返回n1,因为n1现在就是头节点

我们运行一下这个代码,我们发现报错了

为什么会报错呢?其实细心的人已经发现了错误了,就是我们n3出现了空指针的解引用,如下图所示,在我们的最后,我们需要将整体指针向下移动的时候,我们这个n3已经是空指针了,那么当然最后就会出错了

那么该如何解决呢?其实解决方案也很简单,我们这个错误只可能会出现在最后一次的循环中,让指针整体向后移动,但是此时的n3其实是什么样已经没有什么影响了,所以那不妨,当n3为NULL时候,我们就直接让n3不在继续移动即可,我们只需要对最后一部进行一下特殊处理。使用一个if语句即可

如下图所示

当然我们还是运行一下吧,我们发现,还是出错了,但是这次不同的是,我们有两个通过了测试用例,说明我们是有一些极端情况没有考虑到才出的错误

我们想一下,使得程序错误的输入是一开始给了一个空指针,那这样的话n3一开始的定义就出了问题了,它出现了空指针的解引用。那么特殊情况特殊处理,我们使用一个if语句来解决这个问题,因为如果这个链表使空的话,那么它反转之后还是它本身,所以我们直接返回它本身就是了

运行结果为通过。

在这里也给出这种方法的代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head){
    if(head==NULL)
    {
        return head;
    }
    else
    {
            //创建好三个指针
    struct ListNode* n1=NULL;
    struct ListNode* n2=head;
    struct ListNode* n3=head->next;
    //反转指针
    while(n2)
    {
        //反转箭头
        n2->next=n1;
        //所有指针后移
        n1=n2;
        n2=n3;
        if(n3!=NULL)
        {
            n3=n3->next;
        }
    }
    return n1;
    }
}

2.思路二

除了我们直接反转指针以外,我们其实还可以使用头插的方式来实现,我们是这样想的

对于下面这个链表而言

我们定义两个指针,cur和NewHead,一个是直接由head赋值得到,一个是指向NULL

然后我们的想法是这样的直接让cur->next=NewHead,也就是这样子。然后上面的依次往下头插

但是我们很快就发现问题了,那就是如果我一旦改变cur的指向,那么我们就找不到原链表了,所以我们就得一开始就定义一个指针记录下一个结点的地址

然后,我们就让NewHead=cur;next=cur,next也继续往下走

然后我们重复这些流程

上面是一次操作,我们继续

继续往下走

最后我们直接返回NewHead就可以了

代码为

测试结果为

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head)
{
    //创建两个指针
    struct ListNode* cur=head;
    struct ListNode* NewHead=NULL;
    //当cur为空的时候,循环结束
    while(cur)
    {
        //记录下一个结点坐标,这样写的好处是后续不用继续对next进行特殊处理
        struct ListNode* next=cur->next;
        //头插
        cur->next=NewHead;
        //整体移动指针
        NewHead=cur;
        cur=next;
    }
    return NewHead;
}

二、力扣第876题:链表的中间结点

题目链接:876. 链表的中间结点 - 力扣(Leetcode)

题目描述:

1.思路一

首先关于中间的问题,我们肯定知道是要分情况的,有奇数的情况和偶数的情况。那么这样的话,我们最容易想到的思路势必是遍历两遍链表,第一遍是得出有多少个元素,第二遍才是找到中间位置的结点。这种方法很简单,但是时间就慢了很多。

2.思路二

还有一种思路也是十分常用的,那就是快慢指针。我们定义两个指针。一个一次走一步,一个一次走两步,这样我们最后也能找出这个中间结点。我们来画图理解一下

我们先看奇数链表的情况

我们让slow走一步,fast走两步

继续往下走

我们可以看出来,当fast->next==NULL时候,快慢指针就找到了中间结点也就是slow

那我们在来看看偶数的情况

我们可以看出来当fast==NULL时,我们就找到了中间结点(注意我们题目的要求是,如果有两个中间结点,返回第二个结点的值)

所以我们就可以得出代码了

而运行结果当然是可以通过的

代码为

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* middleNode(struct ListNode* head){
    //定义快慢指针
    struct ListNode* slow=head,*fast=head;
    //开始循环走,当fast和fast->next都不为空的时候,就可以接着往下走
    while(fast && fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
    }
    //返回slow即可
    return slow;
}

总结

本小节讲解了两个经典的力扣题,希望大家都能够学会

如果对你有帮助,记得一键三连哦!!!

想看更多精彩的讲解,那么一定要关注我哦!!!

相关文章
【力扣】-- 移除链表元素
【力扣】-- 移除链表元素
219 1
Leetcode第21题(合并两个有序链表)
这篇文章介绍了如何使用非递归和递归方法解决LeetCode第21题,即合并两个有序链表的问题。
265 0
Leetcode第21题(合并两个有序链表)
|
算法
【❤️算法笔记❤️】-每日一刷-19、删除链表的倒数第 N个结点
【❤️算法笔记❤️】-每日一刷-19、删除链表的倒数第 N个结点
324 1
|
算法 Go
【LeetCode 热题100】23:合并 K 个升序链表(详细解析)(Go语言版)
本文详细解析了 LeetCode 热题 23——合并 K 个升序链表的两种解法:优先队列(最小堆)和分治合并。题目要求将多个已排序链表合并为一个升序链表。最小堆方法通过维护节点优先级快速选择最小值,;分治合并则采用归并思想两两合并链表。文章提供了 Go 语言实现代码,并对比分析两种方法的适用场景,帮助读者深入理解链表操作与算法设计。
448 10
|
存储 算法 搜索推荐
链表的中间结点
【10月更文挑战第24天】链表的中间结点是链表操作中的一个重要概念,通过快慢指针法等方法可以高效地找到它。中间结点在数据分割、平衡检测、算法应用等方面都有着重要的意义。在实际编程中,理解和掌握寻找中间结点的方法对于解决链表相关问题具有重要价值。
401 1
LeetCode第二十四题(两两交换链表中的节点)
这篇文章介绍了LeetCode第24题的解法,即如何通过使用三个指针(preNode, curNode, curNextNode)来两两交换链表中的节点,并提供了详细的代码实现。
205 0
LeetCode第二十四题(两两交换链表中的节点)
|
索引
力扣(LeetCode)数据结构练习题(3)------链表
力扣(LeetCode)数据结构练习题(3)------链表
335 0
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
407 6
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
220 6
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
489 2