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

目录

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

1.思路一

2.思路二

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

1.思路一

2.思路二

总结


一、力扣第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;
}


总结

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

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

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

  • 26
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 35
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 35
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青色_忘川

你的鼓励是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值