剑指offer--面试题13

题目:以O(1)的时间复杂度删除单链表中的某个节点

自己所写代码如下:

//以O(1)时间删除链表节点
//要求:单向链表,头指针,待删节点指针

//链表节点
struct ListNode
{
    int m_nValue;
    ListNode* m_pNext;
};
//O(n)的解法:从头遍历,找到pToBeDeleted所指节点的前一个节点再进行删除
void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted)
{
    if(pListHead == NULL || *pListHead == NULL || pToBeDeleted == NULL)
        return;
    
    ListNode* pNode = *pListHead;
    if(pNode == pToBeDeleted)   //对pToBeDeleted指向头结点情况的处理
    {
        *pListHead = (*pListHead)->m_pNext;
        delete pToBeDeleted;
        pToBeDeleted = NULL;
    }
    else
    {
        while(pNode->m_pNext != NULL && pNode->m_pNext != pToBeDeleted)
            pNode = pNode->m_pNext ;

        if(pNode->m_pNext == NULL)
        {
            cout<<"pToBeDeleted不在链表中!"<<endl;
            return;
        }

        pNode->m_pNext = pToBeDeleted->m_pNext;
        delete pToBeDeleted;
        pToBeDeleted = NULL;

    }
    
}

//O(1)的解法:复制后一个节点以覆盖待删节点,再删除重复的后一个节点
void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted)
{
    if(pListHead == NULL || *pListHead == NULL || pToBeDeleted == NULL)
        return;
    
    ListNode* pNode = *pListHead;
    if(pNode == pToBeDeleted)
    {
        *pListHead = (*pListHead)->m_pNext;
        delete pToBeDeleted;
        pToBeDeleted = NULL;
    }
    else
    {
        pNode = pToBeDeleted->m_pNext;
        //排除pToBeDeleted指向尾节点的情形
        if(pNode == NULL)
        {
            pNode = *pListHead;
            while(pNode->m_pNext != pToBeDeleted)
                pNode = pNode->m_pNext ;
            
            pNode->m_pNext = NULL;
            
            delete pToBeDeleted;
            pToBeDeleted = NULL;
        }
        else
        {
            pToBeDeleted->m_nValue = pNode->m_nValue;
            pToBeDeleted->m_pNext = pNode->m_pNext;
            delete pNode;
            pNode = NULL;
        }
        
    }
}

在以上O(1)的代码中,自己的想法有些呆板,具体来说:采用复制覆盖的方法则应考虑的是pToBeDeleted指向尾节点的特殊情况(此时,无法复制!)

而非pToBeDeleted指向头结点的情况(这是O(n)的特殊情况!)!!!

O(n)的方法:需要找前驱节点,所以考虑头结点的特殊情况;

O(1)的方法:需要找后继节点,所以考虑尾节点的特殊情况。

代码修改如下:

//O(1)的解法:复制后一个节点以覆盖待删节点,再删除重复的后一个节点
void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted)
{
    if(pListHead == NULL || *pListHead == NULL || pToBeDeleted == NULL)
        return;
    
    ListNode* pNode = pToBeDeleted->m_pNext;
    //排除pToBeDeleted指向尾节点的情形
    if(pNode == NULL)
    {
        pNode = *pListHead;
        if(pNode == pToBeDeleted)
        {
            delete pToBeDeleted;
            *pListHead = pToBeDeleted = NULL;
        }
        else
        {
            while(pNode->m_pNext != pToBeDeleted)
                pNode = pNode->m_pNext;
        
            pNode->m_pNext = NULL;
            
            delete pToBeDeleted;
            pToBeDeleted = NULL;
        }
        
    }
    else
    {
        pToBeDeleted->m_nValue = pNode->m_nValue;
        pToBeDeleted->m_pNext = pNode->m_pNext;
        delete pNode;
        pNode = NULL;
    }
        
}

和参考代码相一致!赞一个!

总结:1、突破常规思维,删除节点不一定需要从头遍历链表,可以用下一结点复制并覆盖待删节点,最后再删除重复的下一结点。

   2、考虑问题全面性:若待删节点为尾节点,则下一个节点为空;若整个链表仅一个节点,删除后,头结点同时设为NULL。

      这些都需要特殊对待!!!

清醒时做事,糊涂时读书,大怒时睡觉,独处时思考; 做一个幸福的人,读书,旅行,努力工作,关心身体和心情,成为最好的自己 -- 共勉
原文地址:https://www.cnblogs.com/hello-yz/p/3251464.html