链表的中间结点与链表的倒数第k个结点(精美图示详解哦)

文章介绍了如何使用快慢指针一次性遍历链表来找到中间结点和倒数第k个结点。对于中间结点问题,快指针每次前进两步,慢指针前进一步,当快指针到达链表末尾时,慢指针所在位置即为中间结点。对于倒数第k个结点,先让快指针移动k-1步,然后两个指针同步移动,当快指针到达末尾时,慢指针指向的就是倒数第k个结点。这种方法避免了两次遍历链表,提高了效率。
摘要由CSDN通过智能技术生成

引言

在上一篇文章中,介绍了反转链表
我们利用了链表是逻辑连续的特点,逆置了链表的逻辑连接顺序,从而实现反转链表:
戳我查看反转链表详解哦

在本篇文章中,我们将介绍找链表的中间结点与链表的倒数第k个结点:
(由于这两道题目的思路比较简单,就放在一起介绍)
链表的中间结点OJ链接
链表的倒数第k个结点OJ链接

链表的中间结点

题目描述与思路

在这里插入图片描述
这道题要求我们找到链表的中间结点并返回这个中间结点。
若链表有奇数个结点,返回中间结点地址;若链表有偶数个结点,这个链表就有两个中间结点,返回后一个。即如果单链表有5个结点,返回第3个结点的地址;若单链表有6个节点,返回后一个中间结点,即第4个结点。
函数的参数为链表第一个结点的地址。结构体变量与主函数部分已经定义,我们只需要实现接口即可。

对于这道题目,我们当然可以先遍历链表,计算出链表的长度,再运算出中间结点是第几个。然后再遍历到该位置并返回即可。
但是这样的算法显得有些麻烦,有没有一种算法可以实现只遍历一遍就找到中间结点呢?

当然是可以的:
我们可以采用快慢指针的方法来实现:快指针一次向前移动两个结点;慢指针一次向前移动一个结点。当快指针到链表末尾时,慢指针的位置就是单链表的中间结点:

实现

为了使代码更简洁,我们可以对结构体名称重命名:

typedef struct ListNode ListNode;

要实现这个算法,我们首先需要两个指针变量fast与slow,将它们都初始化为单链表头结点的地址:

ListNode* fast = head;
ListNode* slow = head;

然后while循环,每次循环中slow向后移动一个结点;fast向后移动两个结点。

当fast->next为NULL时,即fast已经不能实现向后移动两个结点了,所以此时跳出循环。并且,当fast后面两个结点均不为NULL时,才进行向后移动的操作,否则跳出循环。每次循环,先执行slow指针向后移动一个结点,这样可以使链表长度为偶数时,slow指向中间结点的后一个:
在这里插入图片描述
在这里插入图片描述

typedef struct ListNode ListNode;

struct ListNode* middleNode(struct ListNode* head) 
{
   ListNode* fast = head;
   ListNode* slow = head;
   while (fast->next)
   {
       slow = slow->next;
       if (fast->next->next)
       {
           fast = fast->next->next;
       }
       else
       {
           break;
       }
   }
   return slow;
}

链表的倒数第k个结点

题目描述与思路

在这里插入图片描述
这道题要求我们找到单链表中的倒数第k个结点。

我们当然可以遍历整链表,计算链表的长度。计算出链表的倒数第k个元素的位置后,再遍历找到,并返回。
但是这样的算法显得有些麻烦,我们可以尝试在只遍历一遍的情况下实现:

我们可以使用双指针的方法,先让快指针向后移动k个结点。然后两个指针一起向后移动,当快指针t指向最后一个结点时,慢指针就指向链表的倒数第k个结点。

实现

为了使代码更简洁,我们可以对结构体名称重命名:

typedef struct ListNode ListNode;

要实现这个算法,我们首先需要两个指针变量fast与slow,将它们都初始化为单链表头结点的地址:

ListNode* fast = pListHead;
ListNode* slow = pListHead;

首先判断k是否为0与链表是否为空,如果是,则直接返回NULL;

然后将fast指针向后移动k-1个结点,若fast在向后移动时,fast为NULL说明链表的长度小于k-1,此时返回NULL。我们可以通过在循环之后对fast指针进行判断,从而得知循环是否因链表长度不足而结束。

如果不是,则进入循环,slow指针与fast指针同时向前移动,当fast指针指向链表的最后一个结点时,slow指向的就是倒数第k个元素,返回slow即可。
需要注意的是,此时要求fast指针在链表最后一个结点时停下,所以while的判断雕件为fast->nex,而不是fast:
在这里插入图片描述

typedef struct ListNode ListNode;

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k)
{
    ListNode* fast = pListHead;
    ListNode* slow = pListHead;
    if (k == 0 || pListHead == NULL)
    {
        return NULL;
    }
    while (--k != 0 && fast != NULL)//--k为条件时,循环k-1次
    {
        fast = fast->next;
    }
    if (fast == NULL)
    {
        return NULL;
    }
    else
    {
        while (fast->next)
        {
            slow = slow->next;
            fast = fast->next;
        }
    }
    return slow;
}

总结

当然,这只是其中一种方法,相信还有别的思路。欢迎大家在评论区讨论

还有几道关于单链表的题目讲解,欢迎持续关注哦

如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出

如果本文对你有帮助,希望一键三连哦

希望与大家共同进步哦

评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿qiu不熬夜

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值