剑指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等连续重复的情况也有可能会崩掉,条件过于复杂,废弃.

posted @ 2020-01-15 15:07  virgil_devil  阅读(117)  评论(0编辑  收藏  举报