文章目录

题目描述

输入一个链表,输出该链表中倒数第k个结点。(​​题目来源​​)

拿捏链表(四)—— 链表中倒数第k个结点_代码实现

思路一

由于这道题目并没有要求时间复杂度,我们完全可以先遍历一遍链表,得到链表的结点总数(count),然后再遍历一遍链表,从第一个结点开始,后面的第count - k个结点即为目标结点。

但是在求解过程中有两个情况需要中途便返回NULL:
1.当传入的链表为空时,直接返回空(NULL)。
2.当计算出的链表总结点数count小于k时,返回空(NULL)。

代码实现

struct ListNode {
int val;
struct ListNode *next;
};

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k)
{
if (pListHead == NULL)//判断链表是否为空
return NULL;
struct ListNode* cur = pListHead;//记录当前结点位置
int count = 0;//记录链表的总结点数
while (cur)
{
count++;//结点总数加一
cur = cur->next;//指针后移
}
if (count < k)//count小于k,此时不存在倒数第k个结点
return NULL;
struct ListNode* ret = pListHead;
int pos = count - k;//倒数第k个结点距离第一个结点的结点数
while (pos--)
{
ret = ret->next;//指针后移
}
return ret;//返回目标结点
}

思路二

我们若是仅仅为了解决这个问题,那么用上面这种解法解决这道题完全没问题。但我们若是为了提高自身的代码能力,上面这种思路是远远不够的,在实际运行时该代码的效率不高,而且这种代码在面试时根本不能“打动”面试官。
其实我们还是可以运用“快慢指针”的思想来解决这道题,这样可以使得代码的时间复杂度直接从O(n2)变为O(n) 。需要注意的是:这里所说的“快慢指针”并非一个指针走得快,另一个指针走得慢,而是快指针先走,慢指针在快指针走到某一位置后再开始走。

因为从最后一个结点开始,再往后走一步便是NULL;从倒数第二个结点开始,再往后走两步便是NULL;从倒数第k个结点开始,再往后走k步便是NULL。所以我们可以先让快指针(fast)先走k步,然后慢指针(slow)再和快指针一起往后走,这样,当快指针走到NULL时,慢指针指向的结点就是倒数第k个结点。

拿捏链表(四)—— 链表中倒数第k个结点_链表_02


注意:在快指针(fast)先向后走k步这个过程中,若遇到了NULL,那么说明链表为空,或是k的值大于链表中结点的总数,此时需返回NULL。

代码实现

struct ListNode {
int val;
struct ListNode *next;
};

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k)
{
struct ListNode* fast = pListHead;//快指针
struct ListNode* slow = pListHead;//慢指针
while (k--)//快指针先向后移动k步
{
if (fast == NULL)//快指针移动过程中链表遍历结束,不存在倒数第k个结点
return NULL;
fast = fast->next;//快指针后移
}
while (fast)//快指针遍历完链表时结束遍历
{
fast = fast->next;//快指针后移
slow = slow->next;//慢指针后移
}
return slow;//返回慢指针的值
}