19. Remove Nth Node From End of List
问题描述:Given a linked list, remove the nth node from the end of list and return its head.
题目规定了你可以认为n总是有效的,并尽可能只遍历链表一次就完成操作。
分析时首先可以想到的基本思路是找到倒数第n个节点然后删去,进一步思考后发现寻找倒数第n个节点比较麻烦,但是倒数第n个节点也就是整数第L-n+1个节点,L是整个链表的长度,因此,一个基本的思路就是遍历链表一次,找出链表的长度,在找到第L-n个节点p, 之后p -> next = p-> next -> next;free(p ->next);就可以了。
但是这个方案相当于遍历了链表两次,如何只遍历一次呢?
倒数第n个节点,也就是距离最后一个节点有n-1个节点。思考明白这一层关系,思路就来了,首先通过一个指针first,从head开始向后移动n个节点,此时停下,再设置一个指针second指向head,之后同步的移动first指针和second指针直到到达链表末尾,那么此时,first指针指向链表最后一个节点,second指针指向倒数第n个节点的前一结点。之后通过second -> next = second -> next -> next;free(second -> next)即可。
思路理清楚了,就是代码实现中的细节问题了。
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
struct ListNode* pre = head;
struct ListNode* nex = head;
struct ListNode* ans;
for (int i = 0; i < n; ++i)
{
nex = nex -> next;
}
if (nex == NULL)
{
ans = head -> next;
free(head);
return ans;
}
while (nex -> next != NULL)
{
pre = pre -> next;
nex = nex -> next;
}
free(pre -> next);
pre -> next = pre -> next -> next;
return head;
}
struct ListNode* pre = head;
struct ListNode* nex = head;
struct ListNode* ans;
for (int i = 0; i < n; ++i)
{
nex = nex -> next;
}
if (nex == NULL)
{
ans = head -> next;
free(head);
return ans;
}
while (nex -> next != NULL)
{
pre = pre -> next;
nex = nex -> next;
}
free(pre -> next);
pre -> next = pre -> next -> next;
return head;
}
同时,在题目的讨论区中有一种利用二重指针的方法,我没怎么理解到。http://blogread.cn/it/article/6243?f=wb 转一下这篇文章,以后再细细了解吧。
leetcode 83问题描述:Given a sorted linked list, delete all duplicates such that each element appear only
once
.For example,
Given
Given
Given
1->1->2
, return 1->2
.Given
1->1->2->3->3
, return 1->2->3
.
本题的坑点在于理解题意,本题给出的测试用例的值相同的节点都是相邻的!而自己理解的是值相同的节点的分布式随机的。这就绕了很大的弯路。如果是值相同的点都是相邻的,就很好解决。
struct ListNode* deleteDuplicates(struct ListNode* head) {
if (head)
{
struct ListNode* p = head;
while(p -> next)
{
if (p -> val != p -> next -> val)
{
p = p -> next;
}
else
{
struct ListNode* tmp = p -> next;
p -> next = p -> next -> next;
free(tmp);
}
}
}
return head;
}
if (head)
{
struct ListNode* p = head;
while(p -> next)
{
if (p -> val != p -> next -> val)
{
p = p -> next;
}
else
{
struct ListNode* tmp = p -> next;
p -> next = p -> next -> next;
free(tmp);
}
}
}
return head;
}
代码中标红的代码需要注意,此处只需要将p节点指向在下一节点即可,不要用p = p -> next -> next。写成这样的话,在free了tmp节点之后,原先p节点的next指针就变得不预测,没有指向,造成内存泄漏。
同时,还有一种递归的方法,需要对递归一定的理解和熟悉。观察了上面的方法后,我们发现每一个节点做的动作都是一样的,先判断本节点与下一节点的值是否相同,如果不相同则指针移动到下一节点,如果相同,则移动到下一节点的后一个节点。因此,通过递归的方式了一写出更加简洁的代码。
public ListNode deleteDuplicates(ListNode head) {
if(head == null || head->next == null)return head;
head->next = deleteDuplicates(head->next);
return head->val == head->next->val ? head->next : head;
}
在做leetcode上的试题时,一般不需要将不用的节点free掉或者delete掉,因为只有确定是malloc的节点才需要在不用之后立刻释放点。但是在leetcode的试题中没有这样的表述和要求。因此,不需要再这方面进行纠结。当然严格要求自己是好的,就看自己的习惯了。