题目:
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
思路: 定义两个指针,先移动右指针使两个指针间隔固定长度n,然后将两个指针同步移动到链表最后的节点。最后删除左指针的下一个节点即可。
坑: 考虑到边界情况,即删除的节点是链表的第一个节点或者最后一个节点。第一个节点没有前驱节点,由于要确定删除第一个节点实现和所有的其他情况统一化,解决办法是在链表的头部添加一个前置的节点。
第一次写的代码:
/**
* 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) {
if (!head->next)
{
head = head->next;
return head;
}
ListNode* left = head;
ListNode* right = head;
int curr_idx = 0;
while( right->next ){
right = right->next;
curr_idx ++;
if( curr_idx > n ){
left = left->next;
}
}
if (curr_idx == 1)
{
if (n == 1)
{
head->next=NULL;
delete(right);
return head;
}
if (n == 2)
{
head = head->next;
return head;
}
}
if (curr_idx == 2)
{
if (n == 1)
{
right = right->next;
delete right;
}
if (n == 2)
{
ListNode* m = head->next;
m->val=m->next->val;
m->next=m->next->next;
return head;
}
if (n == 3)
{
head = head->next;
return head;
}
}
if (curr_idx == n-1)
{
head = head->next;
return head;
}
left->next = left->next->next;
return head;
}
};
优化后的代码:
/**
* 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 *pre = new ListNode(0);
pre->next = head;
ListNode *right = pre;
ListNode *left = pre;
for(int i = 0; i < n; i++) {
right = right->next;
}
while(right->next != NULL) {
right = right->next;
left = left->next;
}
ListNode *k = left->next;
if(k == head) {
head = head->next;
delete k;
return head;
}
left->next = k->next;
delete k;
return head;
}
};
总结:
1、删除一个节点的关键是找到该节点的前向节点
2、头节点没有前向节点,就人为的添加一个前向的节点,这样可以使处理所有的情况实现统一,而不用因处理特殊情况引入复杂的开销。