LeetCode-19 删除链表的倒数第N个结点
题目地址
19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
题目解析
-
先分析一下给出的链表的声明
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) {}
};
-
无法事先得知链表的总长度
-
可以考虑将所有的结点的指针按照顺序存放到一个线性表中,通过线性表寻找倒数第n个结点的位置。
代码实现
-
思路比较简单,直接实现
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
vector<ListNode*> node_ptr_copy;
ListNode* iter = head;
while(iter != nullptr){
node_ptr_copy.push_back(iter);
// 结点指针迭代
iter = iter->next;
}
// 此时node_ptr_copy存储了所有结点指针的拷贝
// 倒数第n个结点实际上是正数第node_ptr_copy.size()+1-n
// 对应的索引是node_ptr_copy.size()-n
// 获取目标结点前一结点的指针
int counts = node_ptr_copy.size();
if(counts - n == 0){
// 倒数第n个是首结点,删除首节点,返回下一节点
ListNode* newHead = head->next;
delete head;
return newHead;
}
else if(n == 1){
// 即最后一个元素,修改倒数第二个元素的next并删除最后一个元素即可
delete node_ptr_copy[counts-n-1]->next;
node_ptr_copy[counts-n-1]->next = nullptr;
return head;
}
else{
// 属于中间的元素
// 令目标的前一个结点的next指向目标后一个结点
delete node_ptr_copy[counts-n];
node_ptr_copy[counts-n-1]->next = node_ptr_copy[counts-n+1];
return head;
}
}
};
结果
-
虽然我们一趟循环就解决了问题,但是结果并不是非常优秀,尝试分析示例代码找到可优化的地方
代码优化
-
示例代码
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(head==NULL) return head; // 题目说明了链表的大小大于等于1,则不会出现头节点为空的情况,该句可以不需要
ListNode *first=head, *second=head; // 双指针
// 搜索正数第n+1个结点保存给first
for(int i=0;i<n;i++){
if(first==NULL) return head;
first = first -> next;
}
// 正数第n+1个结点是空结点,表示n等于链表长度,意思就是说删除首节点
if(first==NULL){
first = head;
head = head -> next;
delete first;
return head;
}
// n 小于链表长度,first是正数第n+1个,second是首节点,此时first和second之间的距离就是n,所有现在只需要保持first和second之间的距离,同时向后移动,当first移动到末尾的时候,second也移动到了倒数第n+1的位置。
while(first->next!=NULL){
first = first -> next;
second = second -> next;
}
// 此时first不需要了,用于暂存要删除的结点
first = second -> next;
second -> next = second -> next -> next;
delete first;
return head;
}
};
图像解释
-
原理一目了然,只需要注意循环的判定即可。