剑指offer算法题之单链表的删除结点操作--面试题13:在O(1)时间删除链表结点

  • 题目如下:
    剑指offer算法题之单链表的删除结点操作--面试题13:在O(1)时间删除链表结点_第1张图片

    - 常见删除链表结点的顺序遍历法
    单链表最常见的删除结点操作:就是从链表的头结点开始,顺序遍历查找要删除的结点,找到该结点后删除即可。
    如下图(a)所示链表中,我们想删除链表结点i,可以从链表的头结点a开始顺序遍历 ,发现h结点的m_pNext指针指向的结点就是i结点,于是我们可以安全的删除结点i,把指针指向下一个结点j。指针调整之后我们就可以安全的删除结点i,并保证链表没有断开,这就是顺序查找的思路,因此时间复杂度就是O(n)

    - O(1)的删除结点法
    我们之所以需要从头开始查找,是因为我们需要得到将被删除的结点的前面一个结点。因为在单链表中没有指向前面结点的指针,所以只能从头结点开始顺序查找。

    但是根据本题提供的条件,我们发现,本题提供了删除结点的指针,也就是说删除几点后面的结点很容易找到。那么我们的解决思路是这样的:我们要删除结点i,先把i的下个结点j的值复制到结点i上,然后把j结点删除(也就是把i的指针指向结点j的下个结点,然后删除结点j),这样达到的效果刚好是把结点i删除了。

    上面这种删除方式存在一种特殊情况,当删除的结点在链表的尾部的时候,此结点后面的结点内容是空,因此没办法进行复制,因此这种情况是需要用顺序遍历删除结点的。

    最后注意,如果链表只存在一个结点,删除后我们需要把链表的头结点设置为NULL。

  • 在编写代码过程中,需要注意的问题:
    1、创建链表结点模块,因为链表结点里面包含数据域和指针域,因此需要动态申请空间。
    2、输出链表各结点的模块中,输出函数语句:
    printf(“%d \t”,pNode->m_nKey); 中的’\t’表示后面空四个字符。
    3、由于上面申请了内存空间,为了防止内存泄漏,需要在程序结束时释放内存空间,也就是delete整个链表的每个结点。

    4、O(1)的删除链表结点模块,也是本题考查的主要算法。共分为三种情况:1)删除结点在链表中间 2)删除结点的链表只含一个结点 3)删除结点在链表的末尾。
    1) 首先找到删除结点后驱结点的后驱结点:
    ListNode* pDoubleNext=pNext->m_pNext ; 接着把后驱结点的值复制给删除结点,最后把删除结点的指针指向pDoubleNext 。最后注意释放 (pToBeDeleted->m_nKey)
    2)只有一个结点的情况,直接删除头结点,然后把链表置空。
    3)当删除结点是最后一个结点时,使用顺序查找找到最后一个结点,然后删除即可,同时也注意对删除结点释放空间

  • 列表内容

  • 整个系统(包括链表的建立,链表结点的删除,链表结点的输出)代码如下:
#include "stdafx.h"
#include <list>

struct ListNode
    {
        int m_nKey;
        ListNode* m_pNext;
    };

//创建链表结点
ListNode* CreateListNode(int value)
{
    if(value==NULL)
        exit(1);

    ListNode* pNode=new ListNode;//申请空间存储数据
    pNode->m_nKey=value;
    pNode->m_pNext=NULL;

    return pNode;
}
//链接各结点,生成链表
void ConnectListNodes(ListNode* pNode , ListNode* pNext)
{
    if(pNode==NULL)
        exit(1);

    pNode->m_pNext=pNext;
}
//输出链表各结点
void PrintList(ListNode* pHead)
{
    printf("PrintList starts.\n");
    ListNode* pNode=pHead;
    while(pNode!=NULL)
    {
        printf("%d \t",pNode->m_nKey);
        pNode=pNode->m_pNext;
    }
      printf("\nPrintList ends.\n");
}

//输出链表单个结点
void printListNode(ListNode* pNode)
{
    printf("%d \n",pNode->m_nKey);
}

void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted)
{
    if(!pListHead||!pToBeDeleted)
        return;

    ListNode* pNext=pToBeDeleted->m_pNext;
    //删除的结点不是链表尾结点
    if(pNext->m_nKey)
    {
        ListNode* pDoubleNext=pNext->m_pNext;
        pToBeDeleted->m_nKey=pNext->m_nKey;
        pToBeDeleted->m_pNext=pDoubleNext;

        free(pNext);//

        return ;


    }
    //链表只有一个结点
    else if(* pListHead==pToBeDeleted)
    {
        delete * pListHead;
        pToBeDeleted=NULL;
        * pListHead=NULL;
    }
    //删除的结点是最后一个结点
    else
    {
        ListNode * tmp= * pListHead;
        //ListNode * pretmp;
        //此处排除一个结点的情况,因此判断应该是头结点后一个结点开始的
        while(tmp->m_pNext!=pToBeDeleted)
        {
            // pretmp=tmp;
             tmp=tmp->m_pNext;
        }
        tmp->m_pNext=NULL;
        free(pToBeDeleted);
        return ;

    }

}
//生成测试模块
void Test(ListNode* pHead,ListNode* pDeleteNode)
{
    printf("the original list is : \n");
    PrintList(pHead);

    printf("the node to be delete is : \n");
    printListNode(pDeleteNode);

    DeleteNode( &pHead ,pDeleteNode);//

    printf("the result list is : \n");
    PrintList(pHead);

}


// 链表中有多个结点,删除中间的结点
void Test1()
{
    ListNode* pNode1 = CreateListNode(1);
    ListNode* pNode2 = CreateListNode(2);
    ListNode* pNode3 = CreateListNode(3);
    ListNode* pNode4 = CreateListNode(4);
    ListNode* pNode5 = CreateListNode(5);

    ConnectListNodes(pNode1, pNode2);
    ConnectListNodes(pNode2, pNode3);
    ConnectListNodes(pNode3, pNode4);
    ConnectListNodes(pNode4, pNode5);

    Test(pNode1, pNode3);

    //DestroyList(pNode1);
}


int main()
{
    Test1();

    system("pause");
    return 0;
}

你可能感兴趣的:(算法题,c++,数据结构)