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

来源:互联网 发布:linux 重启apache命令 编辑:程序博客网 时间:2024/06/06 18:09
  • 题目如下:
    这里写图片描述

    - 常见删除链表结点的顺序遍历法
    单链表最常见的删除结点操作:就是从链表的头结点开始,顺序遍历查找要删除的结点,找到该结点后删除即可。
    如下图(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;}
阅读全文
1 0
原创粉丝点击