18 删除链表的节点/重复节点

题目描述: 

题目一:在O(1)时间内删除链表节点 :在给定的单向链表的头指针和一个节点指针,定义一个函数在O(1)时间内删除该节点。

//链表定义
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};

注意:输入提供了要删除节点的指针!!

测试用例: 

1)功能测试(从有多个节点的链表中删除中间、头、尾节点;从只有一个节点的链表中删除唯一的节点)

2)特殊输入测试(头指针为nullptr;指向要删除的节点的指针为nullptr

解题思路:

1)常规做法: 平均时间复杂度为O(n)   面试时不建议使用

遍历到要删除节点的前一个节点pre,将要删除节点的下一个节点赋值给前一个节点。pre->next = pCurrent->next;

2)不访问当前节点的前一个节点:

因为要删除节点的指针知晓,则其下一个节点是可以访问的。将下一个节点的值(val和next)赋值给当前节点,然后删除下一个节点即可

pCurrent->val = pNext->val;

pCurrent->next = pNext->next;

delete pNext;

pNext = nullptr;

题目描述: 

题目二:删除链表中重复的节点

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

注:虽然题目示例给的重复节点的个数都是2,但是题目中并没有明确指出。因此要考虑重复节点为多个的情况。如1->2->3->3->3->4处理后应该是1->2->4

测试用例:

1)功能测试(重复节点位于链表的头部、中间、尾部;链表中没有重复的节点)

2)特殊输入测试(指向链表头节点的指针为nullptr;链表中所有节点都是重复的

解题思路:

1)由于链表是排序的,只要考虑相邻元素值是否相等,相等即重复。

如果当前节点与下一个节点值相同,他们是重复的节点,需要被删除。为了保证删除之后链表仍然是相连的,我们要把当前节点的前一个节点(因此要定义pPreNode)与后面值比当前节点值大的节点相连。

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead)
    {
        //链表为空时
        if(pHead==nullptr)return pHead;

        ListNode* pPreNode = nullptr; //先初始为空
        ListNode* pCurrentNode = pHead;//指向第一个节点
        ListNode* pNext = nullptr;
        while(pCurrentNode!=nullptr){ //pCurrentNode->next!=nullptr
           // ListNode* pNext = pCurrentNode->next;  //pNext可能为空  //不要放到循环里面定义,每次循环都会定义
            pNext = pCurrentNode->next;  //pNext可能为空
            if(pNext!=nullptr && pCurrentNode->val==pNext->val){  //pNext不为空时,才可以访问其val值,即pNext->val存在
                //有重复节点
                //删除重复节点
                int value = pCurrentNode->val;
                ListNode* pNodeToDel = pCurrentNode;
                while(pNodeToDel!=nullptr && pNodeToDel->val==value){  //循环,以遍历多个连续的重复值
                    pNext = pNodeToDel->next;   // pNodeToDel会被删除,要记录一下它的下一个节点,否则之后就访问不到了
                    delete pNodeToDel;
                    pNodeToDel = nullptr;
                    pNodeToDel = pNext; //移动到下一个节点
                }
                //重新连接链表
                if(pPreNode == nullptr){ //说明时头节点
                    pHead = pNext;
                }else{ //非头节点
                    pPreNode->next = pNext; //not pPreNode = pNext; 由于写错,总报段错误!!! 不能把pPreNode直接指向pNext
                }
                pCurrentNode = pNext; //别忘了也要更新 pCurrentNode
            }else{
                //没有重复节点
                pPreNode = pCurrentNode;
                //pCurrentNode = pNext;
                pCurrentNode = pCurrentNode->next;
            }   //无论是否删除节点,每一次都要更新pPreNode与pCurrentNode,pNext会在每次循环初被更新
        }

        return pHead;
    }
};  

注意:

[1] delete指针后一定先将指针置空。因为不确定后面是否会在使用指针(会赋值),如果不置空,使用未定义的指针是十分危险的行为。

[2] 要区分删除的节点是否是头节点:line36和line38。因为连接的操作是不同的

2)为了统一删除节点后,头节点与其他节点的连接关系。创建一个新的指针指向头指针。

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead)
    {

        if(pHead==nullptr || pHead->next==nullptr) return pHead;

        ListNode* newpHead =new ListNode(-1);
        newpHead->next = pHead;

        ListNode* pPreNode = newpHead;
        ListNode* pNode = pHead;
        ListNode* pNext = nullptr; 

        while(pNode!=nullptr){
            pNext = pNode->next;
            if(pNext!=nullptr && pNode->val==pNext->val){
                //有重复节点
                ListNode* pToDel = pNode;
                int value = pNode->val;
                while(pToDel!=nullptr && pToDel->val==value){
                    pNext =  pToDel->next;
                    delete  pToDel;
                    pToDel = nullptr;
                    pToDel = pNext;
                }
                pPreNode->next = pNext;
                pNode = pNext; //勿忘
            }else{
                //没有重复节点
                pPreNode = pNode;
                pNode = pNext;
            }
        }
        return newpHead->next;

    }
};

3)使用递归方法:

当头节点重复时,直接移动头节点到第一个不重复的节点。头节点不重复时,移动到下一个节点,剩余部分递归调用。这样链表可以只处理头节点重复的情况。

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead)
    {
        //递归的终止条件
        if(pHead==nullptr || pHead->next==nullptr) return pHead;

        ListNode* pNode = nullptr; 

        if(pHead->val==pHead->next->val){
            //链表的头节点有重复
            int value = pHead->val;

            while(pHead!=nullptr && pHead->val==value){
                pNode = pHead->next;
                delete pHead;
                pHead=nullptr;
                pHead=pNode;
            }

            return deleteDuplication(pHead);  //因为输入的是头节点,没有next指针接收,因此直接return

        }else{
            //链表不重复
            pHead->next = deleteDuplication(pHead->next); //返回的链表应该连接在头指针的下面
        }
        return pHead;

    }
};

  

  

原文地址:https://www.cnblogs.com/GuoXinxin/p/10421144.html

时间: 2024-11-10 14:50:37

18 删除链表的节点/重复节点的相关文章

[华为机试练习题]24.删除链表中的重复节点、剩余节点逆序输出

题目 描述: 题目描述: 输入一个不带头节点的单向链表(链表的节点数小于100),删除链表中内容重复的节点(重复的节点全部删除),剩余的节点逆序倒排. 要求实现函数: void vChanProcess(strNode * pstrIn,strNode * pstrOut); [输入] pstrIn:输入一个不带头节点的单向链表 [输出] pstrOut:删除内容重复的节点(重复的节点全部删除),剩余节点逆序输出(不带头节点,链表第一个节点的内存已经申请). [注意]只需要完成该函数功能算法,中

删除链表中的重复节点、剩余节点逆序输出

#include <stdlib.h> #include <algorithm> #include <functional> #include <iostream> #include "oj.h" using namespace std; /* 功能: 输入一个不带头节点的单向链表(链表的节点数小于100),删除链表中内容重复的节点(重复的节点全部删除),剩余的节点逆序倒排. 输入: pstrIn: 输入一个不带头节点的单向链表 输出:

删除链表中的重复节点

这里的删除是只要有重复就要全部删除,如1->2->2->3,删除之后就是1->3. #include<stdio.h> #include<stdlib.h> struct ListNode { int val; struct ListNode *next; }; struct ListNode* deleteDuplicates(struct ListNode* head) { if(NULL==head||NULL==head->next) retu

[华为机试练习题]24.删除链表中的反复节点、剩余节点逆序输出

题目 描写叙述: 题目描写叙述: 输入一个不带头节点的单向链表(链表的节点数小于100),删除链表中内容反复的节点(反复的节点所有删除),剩余的节点逆序倒排. 要求实现函数: void vChanProcess(strNode * pstrIn,strNode * pstrOut); [输入] pstrIn:输入一个不带头节点的单向链表 [输出] pstrOut:删除内容反复的节点(反复的节点所有删除).剩余节点逆序输出(不带头节点,链表第一个节点的内存已经申请). [注意]仅仅须要完毕该函数功

如何删除链表中值重复的节点

前言 ??最近在刷<剑指offer>的题,其中有一道题目叫做删除链表中重复的节点,我想了半天没想到比较好的解决办法,于是看了看大佬的解析(菜哭了).不看不知道,一看吓一跳,这尼玛写的也太妙了,忍不住写篇博客记录一下这个解题思路和代码. 题目描述 ??在一个排好序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5. 解题思路 ??这道题我

老虎-删除排序链表中的重复节点

题目:1-1-2-3-3-3-4-5-6-6-7,删除重复节点后返回2-4-5-7 def delete_duplicate(head): h = Node(0) k = h p = head if not p or not p.next: return head q = p.next if p.val!=q.val: k.next = p k = k.next while q.next: if q.val!=p.val and q.val!=q.next.val: k.next = q k =

【剑指offer】18.删除链表中重复节点

思路 实现 /* 说下大概思路: 与链表的其他题目类似,为了防止删除头结点的极端情况发生,先创建空结点dummy,使dummy指向传入的head结点. 然后创建cur的指针,指向链表的头部(即dummy). 接着对cur指针迭代,因为要对比cur(cur最初始的定义指向空结点)指针的下一个结点与下下一个结点的值是否相等,为了防止产生空指针异常,故退出迭代的条件为:cur.next != null && cur.next.next != null. 在迭代过程中,如果cur.next.val

83删除链表中的重复元素

1.思路 吸取了前几道题设置头节点重新插入的想法 建立空的头节点,和一个指向结点的指针p用来控制插入的位置 判断第一个值和第二个值是否相等相等,链表头指针向后移动,直至不等,把head结点插入到空结点后面,直至完成 2.错误 错误提示:Line 16: Char 18: runtime error: member access within null pointer of type 'struct ListNode' (solution.cpp) 错误原因:没有判断当前指针是否指向了一个有意义的

LintCode Python 简单级题目 112.删除链表中的重复元素

题目描述: 给定一个排序链表,删除所有重复的元素每个元素只留下一个. 您在真实的面试中是否遇到过这个题? Yes 样例 给出 1->1->2->null,返回 1->2->null 给出 1->1->2->3->3->null,返回 1->2->3->null 标签 链表 题目分析: 给定一个排序链表,删除所有重复的元素每个元素只留下一个. 源码: """ Definition of ListNod