1.题目
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
2.示例
3.代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
//如果只有一个节点,只能删除这个节点,就直接返回NULL
if(head->next==NULL)
return NULL;
//计算节点的个数
int num=0;
struct ListNode *q=head;
while(q!=NULL)
{
num++;
q=q->next;
}
//如果num==n表示删除的就是head节点
if(num==n)
{
head->val=head->next->val;
head->next=head->next->next;
return head;
}
//删除除了head节点之外的其他节点,就先找到要删除的节点的前一个节点
struct ListNode *p=head;
for(int i=1;i<num-n;i++)
{
p=p->next;
}
p->next=p->next->next;//然后直接把这个节点的next指向要删除的节点的下一个节点
return head;
}
4.进阶:你能尝试使用一趟扫描实现吗?
(1)分析:
假如我们有一条长度为L的绳子,如下图:
现在在离head头n长度处有一个p1点,head处也有一个p2点:
假设现在,p1与p2都同时向后移动,移动的速度是相同的,那么当p1点到达末尾时,p2点也刚好到达离末尾只有n长度处:
根据上面的分析:
可以很清楚的知道,只要定义两个指针p1与p2,让他们之间的距离保存n,只要其中一个点到达末尾,另一个点一定离末尾也就只有n的距离,这就是解决这道题的思路。
(2)代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
//如果只有一个节点,只能删除这个节点,就直接返回NULL
if(head->next==NULL)
return NULL;
//使用双指针移动,p1每一轮循环都会向后移动;p2只有当p2与p1的距离刚好是i-j==n时,才会向后移动
struct ListNode*p1=head;
struct ListNode*p2=head;
int i=1,j=1;
//用于记录第n个节点的前一个节点
struct ListNode*p_pre=head;
while(true)
{
//判断是是否要移动p2指针
if(i-j==n)
{
p_pre=p2;
p2=p2->next;
j++;
}
//p1指针每次都会向后移动
p1=p1->next;
i++;
//如果p1已经到了联尾了
if(p1==NULL)
{
if((p_pre==head)&&(j==1))//如果此时满足这个条件,那么在整个while循环过程中是没有移动过p2指针的,例如这种情况:[1,2,3],n=3,表示删除第一个节点head
{
head->val=head->next->val;//把头节点的下一个节点的值复制给头节点
head->next=head->next->next;//然后把头节点连接到第一个节点的下一个节点的下一个节点,这样就相当于把原先头节点的下一个节点当作新的头节点了
}
else//如果不复合上面的情况,那么链表删除的一定是非第一个节点,,那么此时的p_pre一定指向最后一个节点的前一个节点,而p2一定指向最后一个节点
{
p_pre->next=p2->next;//那么这里直接用要删除节点的前一个节点连接要删除节点的下一个节点就行了
}
break;
}
}
return head;
}
5.总结
第一种方法是很普通的方法。第二种方法需要使用双指针,并需要一些思考,编程并不复杂。