剑指OFFER 删除链表中重复的结点
剑指OFFER 删除链表中重复的结点
题目描述:
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。
例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
分析一下题目,关键点 1.已排序的链表 2.删除重复的结点
一种比较次的解法
一开始想不出来,实在没办法了就只能新建一个链表来存储结果,虽然通过了,但是在工作中处理这样的问题时候千万不要在函数里使用malloc,能不用就不用(容易内存泄露)
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
map<int,int> m;
ListNode* deleteDuplication(ListNode* pHead)
{
if(pHead == NULL)return NULL;
ListNode* node = pHead;
ListNode* new_head = NULL;
while(node != NULL)
{
m[node->val]++;
node = node->next;
}
ListNode* last_node = NULL;
auto it = m.begin();
while(it != m.end())
{
//把节点添加到新的list当中
if(it->second == 1)
{
if(new_head == NULL)
{
new_head = (ListNode*)malloc(sizeof(ListNode));
new_head->val = it->first;
new_head->next = NULL;
last_node = new_head;
}else{
ListNode* node;
node = (ListNode*)malloc(sizeof(ListNode));
node->val = it->first;
node->next = NULL;
last_node->next = node;
last_node = node;
}
}
it++;
}
return new_head;
}
};
还行的解法
先遍历一遍链表,用map标记需要删除的结点.
然后再遍历一遍,把要删的结点删除掉,思路比较清晰.
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
map<int,int> m;
ListNode* deleteDuplication(ListNode* pHead)
{
if(pHead == NULL)return NULL;//如果没有结点
ListNode* node = pHead;
while(node != NULL)
{
m[node->val]++;
node = node->next;
}
node = pHead;
ListNode* last_node = pHead;
while(node != NULL)
{
if(m[node->val]>1)
{
//如果是头结点需要分别对待,因为此时状态中last_node是无效的
if(node == pHead)
{
pHead = pHead->next;
node = pHead;
}else{
last_node->next = node->next;
node = last_node->next;
}
continue;
}
//状态转移
last_node = node;
node = node->next;
}
return pHead;
}
};
反思:
没有用到题目中已排序的条件.上面的解法拓宽了题目要求的范围.效率上会有些损失.
那么,有没有更好的解法?
应该是有的,通过仅仅一次遍历就可以删除掉重复的元素,但是需要记录前面结点,而且条件判断十分复杂,按着这种思路写了几遍代码,无奈总是有特殊情况没包含进去,放弃了.
代码先贴这
ListNode* deleteDuplication(ListNode* pHead)
{
if(pHead == NULL)return NULL;//如果没有结点
if(pHead->next == NULL)return pHead;//如果只有一个结点
if(pHead->next->next == NULL )//如果只有两个结点
{
if(pHead->val == pHead->next->val){
return NULL;
}else{
return pHead;
}
}
//如果进入到这里,说明至少有三个结点存在
//上上个结点,初始化时为头结点(第一个结点)
ListNode* last_last_node = pHead;
//上一个节点,初始化时为第二个结点
ListNode* last_node = pHead->next;
//当前结点,初始化时为第三个结点
ListNode* node = pHead->next->next;
while(last_node != NULL && node != NULL)//这里以防万一判断两次
{
if(last_node->val == node->val)//出现重复结点,那么last_last_node后面的全部删掉
{
//这里last_last_node的值不等于last_node/node的值
//而last_node的值等于node的值
ListNode* t_node = last_node;//last_node这里是重复的开头,后面所有重复的都要去掉
//找到后面那个不重复的结点
while(t_node != NULL && t_node->val == node->val)
{
t_node = t_node->next;
}
last_last_node->next = t_node;
//删除后需要恢复last_node和node的状态
last_node = t_node;
if(t_node != NULL)
{
node = last_node->next;
}else{
node = NULL;
}
continue;
}
//更新状态
last_last_node = last_node;
last_node = node;
node = node->next;
}
return pHead;
}
上面的代码,当样例为 1 1 1 2 3 4
开头就出现重复时无法通过. 后来经过思考,即使处理了开头重复的问题在遇到类似1 1 1 1 2 2 2 2
等连续重复的情况也有可能会崩掉,条件过于复杂,废弃.