题目
代码
class Solution { public: ListNode* removeNthFromEnd(ListNode* head, int n) { ListNode* dummyHead = new ListNode(0); //因为删除的可能是头结点,所以用虚拟头结点更方便,避免分类讨论 dummyHead->next = head; ListNode* slow = dummyHead; ListNode* fast = dummyHead; while(fast!=NULL&&n--){//防止操作空指针 fast=fast->next; } fast=fast->next; while(fast!=NULL){ fast=fast->next; slow=slow->next; } slow->next=slow->next->next;//删节点操作 return dummyHead->next; } };思路
快慢指针结合虚拟头结点。利用快慢指针实现对链表的遍历,解决链表无法定位“倒数第N个结点”的痛点,因为当快指针到达链表末尾的时候,慢指针正好位于要删除的结点的前一个~我们直接一个 slow->next=slow->next->next;就可以实现删除操作啦~至于虚拟头结点的存在,是为了避免链表只有一个结点的情况,这样的话就不需要特殊讨论了,有个dummyhead比较方便统一写代码~
小结
1.为啥会出现多出来的一行fast=fast->next?让fast实际比slow多走n+1步,因为最终我们的slow其实是在被删除结点前的位置哦~
2. while(fast!=NULL&&n--)指的是: fast 不为空且n>0
3.快慢指针一般用于什么时候?
✅️链表,不知道长度,要找倒数 / 中间位置(本题就是显而易见啊!!)
✅️判断链表有没有环(这个有印象!之前数据结构里有个约瑟夫问题感觉就能用上)
✅️要求链表一趟遍历完成,不能扫两遍
4.虚拟头结点什么时候用?什么时候不用?
| 题目行为 | 是否推荐 dummyHead |
|---|---|
| 删除结点,有可能删原头 | ✅ 用 |
| 新建链表,尾插法拼接结点 | ✅ 用 |
| 查询、遍历、求长度、找中点、判环 | ❌ 不用 |
| 链表反转 | 可选(大部分写法不用) |