力扣对应题目链接:LCR 140. 训练计划 II - 力扣(LeetCode)
核心考点 :链表,前后指针的使用,边界条件检测。
一、《剑指Offer》内容
二、分析问题
较优解题思路:
- 题目中的链表是单链表,也就不能从后往前进行。
- 可以定义两个指针(快慢双指针),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 指针遍历快一些(比如一次在链表上走两步),或者让它先在链表上走若干步。