1、题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
2、代码1:
2.1 原理:首先判断第一个节点是不是值重复的结点,如果是,找到第一个不重复的结点作为头指针,重新调用函数即可;如果第一个节点不是值重复的结点,那么只需要删除以头结点下一个节点为头结点的链表中的重复的元素就可以了,这里也是一个递归调用。
2.2 代码1:
//递归的版本
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead)
{
if(pHead==NULL)
return NULL;
ListNode *p = pHead;
ListNode *pLast = NULL;
//第一个节点就重复了
if((p->next)&&(p->val==p->next->val))
{
pLast = p;
while(p!=NULL && p->val==pLast->val)
p=p->next;
return deleteDuplication(p);
}
//第一个节点没有重复
else
p->next = deleteDuplication(p->next);
return pHead;
}
};
3、代码2:
3.1 原理:首先判断第一个节点是不是值重复的结点,如果是,找到第一个不重复的结点作为头指针,重新调用函数即可;不是,则判断头结点的下一个结点是不是重复,重复就记录下一个节点的值,知道找到后面节点中和头指针下一个节点值不同的结点,将头指针指向该结点,继续遍历,只要完成遍历。
3.2 代码2:
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead)
{
if(pHead==NULL)
return NULL;
ListNode *p = pHead;
ListNode *pLast = NULL;
//第一个节点就重复了
if((p->next)&&(p->val==p->next->val))
{
pLast = p;
while(p!=NULL && p->val==pLast->val)
p=p->next;
return deleteDuplication(p);
}
// 第一个节点没有重复
else
{
while(p)
{
//记录当前节点
pLast = p;
//判断其下一个节点是否重复
if(p->next && p->next->next && p->next->val==p->next->next->val)
{
//指向重复的第一个节点
p = p->next;
//重复的值
int val = p->val;
//后面结点的值如果等于重复的值
while(p->val==val)
{
p=p->next;
if(p==NULL)
break;
}
//p指向后面第一个值不等于重复的值的结点
//将当前节点指向后面第一个值不等于重复的值的结点
pLast->next=p;
//这里重要:无法判断当前第一个值不等于重复的值的结点
//是不是重复的,因此当前节点不变
// 比如122334..如果没有这句话,输出就是1334
p = pLast;
}
//下一个节点不是重复的值
else
p=p->next;
}
return pHead;
}
}
};
4、总结
(1)做链表的题目的时候:无论什么时候都需要第一时间考虑递归的思想,因为递归可以优化代码,链表的结构本身就很适合做递归。
(2)链表操作:特别访问节点的值或者是访问节点的next指针的时候,一定要先判断节点的存在性以及next节点的存在性。先判断节点的存在的性,再进行访问,否则很容易造成内存访问错误。
(3)遇到复杂链表的操作:一定要将各种可能出现情况考虑清楚,再将一些可能出现的情况合并分析和操作。
(4)在做链表的遍历的时候:一定要分清楚,遍历操作的当前节点是哪个节点,不要到后面乱了。以及遇到for以及while的时候,一定要注意终止条件,不能造成无限的循环。**
比如上面代码2中的while循环中:
//正确写法
while(p->val==val)
{
p=p->next;
if(p==NULL)
break;
}
//还有一种很典型的但是不容易找出错误的写法:
while(p->val==val)
{
if(p->next)
p = p->next;
}
后面这种看起来没什么问题,访问p—>next的时候,
先判断存在性,但是如果其不存在,这个while将一直执行。