关于Leetcode的刷题方法,在网上看了很多,每个人都有着不同的见解。作为Leetcode小白,我还是决定用最原始的方法进行刷题,即把每一题的思路都记录下来,虽然比较耗时间,但是个人感觉这样子的方法会让我对题目有更深入的了解。希望能在该专栏中记录下刷题过程中遇到的大大小小的问题🍊。
题目描述
解题思路
哑指针的重要性
- 假设我们要删除head本身,由于在链表中,如果要删除当前元素,一定要获取到它的前一个位置才能删除,因此我们可以引入哑指针的思想,即让一个dumy指针指向head,之样子就能够应付一些特殊情况
两次遍历法
- 由于题目没有告诉我们链表的长度,因此最直观的想法就是先通过一次遍历找到链表的长度是多少
- 第二次遍历再去删除倒数第n个元素即可
一次遍历法(双指针)
- 我们可以构建两个指针,left指针指向哑指针dummy,right指针在left指针的右边n个位置
- 然后同时移动指针left和指针right,当right移到最后一个位置的时候,此时left在倒数第n-1个位置,即要删除元素的前面,这时候直接删除掉即可
例子讲解
- 比如下面的例子,n=2,表示要删除倒数第二个元素”4“
- 首先我们构建一个哑指针,令其指向head
- 然后创建left指针指向哑指针,right指针位于left指针的后n个位置(由于题目说n有效,因此不用考虑越界问题)
- 然后同时移动left和right指针
- 当right指针位于最后一个元素的时候(right->next == NULL),删除left指针的下一个元素即可
- 最后再返回head指针(哑指针的后一位)
题目代码
1.两次遍历
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode*dummy = new ListNode{0};
dummy->next = head;
int len = 0;
while(head){ //第一次遍历求链表长度
len++;
head = head->next;
}
ListNode *temp = dummy;
int num = len - n;
while(num--){ //指向待删除元素的前一个位置
temp = temp->next;
}
temp->next = temp->next->next;//进行删除即可
return dummy->next;
}
};
2.一次遍历(双指针法)
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode*dummy = new ListNode{0,head}; //构建指向head的哑指针
ListNode*right = dummy,*left = dummy; //构建left指针和right指针
while(n--){ //让right指针位于left指针的后n位
right = right->next;
}
while(right->next){ //同时移动两个指针,直到right指针位于最后一个位置
left = left->next;
right = right->next;
}
left->next = left->next->next; //删除倒数第n个元素
return dummy->next; //返回head指针
}
};