题目描述
题目链接:删除链表中重复的节点
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
举个例子
解题思路:
删除掉重复节点,这个和去重有些不一样,去重是保留一个,这里是要有重复的完全删除掉。第一感觉就使用双指针,因为肯定要进行前后比较。两个节点对应的值不相同就往后走,相同就让后面指针自己走,直到不相同。然后删除掉之前的节点。
图解实现:
定义两个指针cur和next进行指向,假如两者对应的值不相等,就同时向后走
假如走到cur与next对应的值相等之后停下cur,让next继续走直到两者的值重新不相等
这时候我遇到的问题是,假如保存cur的下一个节点,循环进行free。那么1,2两个几点怎么办,所以再次定义一个prev节点用于保存节点2。
这样我们就可以放心的free掉重复的值,再让prev->next直接指向next就行了。
当然进行前一步操作之后,next也要移动到下一个直至next为空
走到这其实我们主要步骤已经完成,中间部分的逻辑是没有问题的,但是单链表我们必须考虑它的极端情况,只要中间逻辑不错,出问题只会出在头和尾
先看头的情况:
如果像上图一样free掉cur,那么此时prev还是空,NULL->Next=Next,你对空指针进行了操作。
再看第二种尾的情况:
现在我们对第一种头的情况处理:
经过前面分析我们确定是prev->next=next;出了问题,那么我们现在对它进行操作,他是空出了问题
我们就写一条if语句,if不为空,正常进行操作,else为空处理它;
if(prev)
{
prev->next=next;
}
//这是极端情况1,假如头结点重复导致走到这里prev还是空
else
{
//让next这个位置成为头
pHead=next;
}
现在对第二种尾的情况分析:
经过前面作图,尾重复的时候,由于迭代条件next=next->next,一直让他运行了下去,那么我们设置一个条件让他为NULL的时候就不要进循环了
//假如是尾上重复,因为是再循环里,next走到停不下来,我们让他NULL时候不进while,就停下了
while(next && cur->val == next->val)
尾上重复的情况,走到最后一次进循环
//尾上重复的情况,最后一次进循环这里是空
next=next->next;
next为空,这一步就不要运行了,因为他是为了下一步后面如果有不相等的值,继续做迭代条件。这是尾上重复的情况,后面没有值了
//极端情况2这里空就不要运行了
if(next)
{
next=next->next;
}
完整代码:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead)
{
if(pHead==NULL||pHead->next==NULL)
return pHead;
ListNode* prev=NULL;
ListNode* cur=pHead;
ListNode* next=cur->next;
while(next)
{
//不相等同时向后走
if(cur->val!=next->val)
{
prev=cur;
cur=next;
next=next->next;
}
else{
//写成if语句只走了一下
//相等让cur停下来,next走到一个不相等的地方,那假如后面一直相等是不是一直走
//prev把他自己和next连起来
//free中间的节点,这个时候时候cur走到了next的地方
//让next移动到下一个位置
//假如是尾上重复,因为是再循环里,next走到停不下来,我们让他NULL时候不进while,就停下了
while(next && cur->val == next->val)
{
//尾上重复的情况,最后一次进循环这里是空
next=next->next;
}
if(prev){
prev->next=next;}
//这是极端情况1,假如头结点重复导致走到这里prev还是空
else{
//让next这个位置成为头
pHead=next;
}
//释放跳过的节点,cur此时也在走
while(cur!=next)
{
//每次循环完都等于下一个节点
ListNode* del=cur;
cur=cur->next;
free(del);
}
//尾上重复的那种情况,因为上面进行过一次next=next->next他已经是空了
//极端情况2这里空就不要运行了
if(next)
{
next=next->next;
}
}
}
return pHead;
}
};
最后简单总结一下,代码不是很难,只要把逻辑想清楚就可以一步一步写出来,但是对于链表,我们要有这种思想,先写出自己的逻辑,然后判断极端情况,而这种极端情况大部分都是由头和尾异常引起的,我们多多思考这种测试用例进行画图,然后才是编写代码。