【一刷《剑指Offer》】面试题 15:链表中倒数第 k 个结点

简介: 【一刷《剑指Offer》】面试题 15:链表中倒数第 k 个结点

力扣对应题目链接:LCR 140. 训练计划 II - 力扣(LeetCode)

核心考点 :链表,前后指针的使用,边界条件检测。


一、《剑指Offer》内容


二、分析问题

较优解题思路:

  1. 题目中的链表是单链表,也就不能从后往前进行。
  2. 可以定义两个指针(快慢双指针),fast 指针先走 k 步,再让 slow 指针跟在后面,使用 “前后指针” 的方式,当 fast 指针到达结尾,slow 指针也就是倒数第 k 个节点。(其实原理就是:fast 指针和 slow 指针之间一直保持着 k 的距离)

三、代码

//力扣AC代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* trainingPlan(ListNode* head, int cnt) {
        ListNode* fast=head;
        ListNode* slow=head;
        while(cnt>0 && fast!=nullptr)
        {
            fast=fast->next;
            cnt--;
        }
        while(fast!=nullptr)
        {
            fast=fast->next;
            slow=slow->next;
        }
        return slow;
    }
};
 
//严谨写法
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* trainingPlan(ListNode* head, int cnt) {
        if(head==nullptr || cnt<0) return head;
        ListNode* fast=head;
        ListNode* slow=head;
        while(cnt>0 && fast!=nullptr)
        {
            fast=fast->next;
            cnt--;
        }
        while(fast!=nullptr)
        {
            fast=fast->next;
            slow=slow->next;
        }
        if(cnt>0) return nullptr;
        else return slow;
    }
};

四、扩展

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


1、分析题目

  • 如果链表中结点总数为奇数,返回中间结点。
  • 如果结点总数是偶数,返回中间两个结点的任意一个。

为了解决这个问题,我们也可以向上面那道题一样定义一个 fast 和 slow 指针,同时从链表的头结点出发,slow 指针一次走一步,fast 指针一次走两步。当 fast 指针走到链表的末尾时,slow 指针正好在链表的中间。


2、代码

//力扣AC代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* middleNode(ListNode* head) {
        ListNode* slow=head;
        ListNode* fast=head;
        while(fast && fast->next)
        {
            slow=slow->next;
            fast=fast->next->next;
        }
        return slow;
    }
};

力扣对应题目链接:141. 环形链表 - 力扣(LeetCode)


1、分析题目

这道题也一样,定义两个指针 fast 和 slow,同时从链表的头结点出发,slow 指针一次走一步,fast 指针一次走两步。

  • 如果 fast 指针追上了 slow 指针,那么链表就是环形链表。
  • 如果 fast 的指针走到了链表的末尾(m_pNext 指向 NULL)都没有追上 slow 指针,那么链表就不是环形链表。

2、代码

(1)写法一(推荐)

//力扣AC代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode* slow=head;
        ListNode* fast=head;
        while(fast && fast->next)
        {
            slow=slow->next;
            fast=fast->next->next;
            if(fast==slow) return true;
        }
        return false;
    }
};

(2)写法二

//力扣AC代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(head==NULL || head->next==NULL) return false;
        ListNode* slow=head;
        ListNode* fast=head->next;
        while(fast!=slow)
        {
            if(fast==NULL || fast->next==NULL) return false;
            slow=slow->next;
            fast=fast->next->next;
        }
        return true;
    }
};

五、举一反三

当我们用一个指针遍历链表不能解决问题的时候,可以尝试用两个指针(快慢双指针)来遍历链表。可以让 fast 指针遍历快一些(比如一次在链表上走两步),或者让它先在链表上走若干步。

 


相关文章
|
1月前
|
算法
【❤️算法笔记❤️】-每日一刷-19、删除链表的倒数第 N个结点
【❤️算法笔记❤️】-每日一刷-19、删除链表的倒数第 N个结点
69 1
|
25天前
|
存储 算法 搜索推荐
链表的中间结点
【10月更文挑战第24天】链表的中间结点是链表操作中的一个重要概念,通过快慢指针法等方法可以高效地找到它。中间结点在数据分割、平衡检测、算法应用等方面都有着重要的意义。在实际编程中,理解和掌握寻找中间结点的方法对于解决链表相关问题具有重要价值。
14 1
|
1月前
(剑指offer)18、删除链表的节点—22、链表中倒数第K个节点—25、合并两个排序的链表—52、两个链表的第一个公共节点(2021.12.07)
(剑指offer)18、删除链表的节点—22、链表中倒数第K个节点—25、合并两个排序的链表—52、两个链表的第一个公共节点(2021.12.07)
49 0
|
6月前
【移除链表元素】LeetCode第203题讲解
【移除链表元素】LeetCode第203题讲解
|
5月前
|
存储 SQL 算法
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
|
5月前
|
存储 SQL 算法
LeetCode 题目 86:分隔链表
LeetCode 题目 86:分隔链表
|
5月前
|
存储 算法 Java
【经典算法】Leetcode 141. 环形链表(Java/C/Python3实现含注释说明,Easy)
【经典算法】Leetcode 141. 环形链表(Java/C/Python3实现含注释说明,Easy)
56 2
|
6月前
<数据结构>五道LeetCode链表题分析.环形链表,反转链表,合并链表,找中间节点.
<数据结构>五道LeetCode链表题分析.环形链表,反转链表,合并链表,找中间节点
53 1
|
5月前
|
算法
【经典LeetCode算法题目专栏分类】【第7期】快慢指针与链表
【经典LeetCode算法题目专栏分类】【第7期】快慢指针与链表
|
5月前
|
存储 SQL 算法
LeetCode 83题:删除排序链表中的重复元素【面试】
LeetCode 83题:删除排序链表中的重复元素【面试】
下一篇
无影云桌面