zoukankan      html  css  js  c++  java
  • 【剑指offer】面试题13、在 O(1)时间上删除链表结点

    题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点。链表结点与函数的定义如下:
    typedef struct Node
    {
        int m_nValue;
        struct Node *m_pNext;
    }ListNode, *pListNode;
    
    void DeleteNode(ListNode **pHead, ListNode *pToBeDeleted);

      在单链表中删除一个结点,最常用的做法莫过于从链表的头结点开始,顺序遍历查找并找到要删除的结点,然后再进行删除操作。

      比如在下图(a)所示的链表中,我们想要删除结点 i,我们就可以从头结点开始遍历该链表,遍历到 h 结点时,会发现 h 的下一个结点 i 就是我们所要删除的结点。这样我们可以把 h 的 m_pNext指向 i 的下个结点 j。指针调整之后我们就可以安全删除结点 i 并保证链表没有断开

    这种顺序查找的时间复杂度为 O(n)。

     

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

    那么是不是一定需要得到被删除结点的前一个结点呢?答案是否定的。以上面(a)链表为例。我们可以很方便的得到要删除结点 i 的下一个结点 j。如果我们把下一个结点 j 的内容复制到欲删除结点 i 上,再把下个结点 j 删除,这样不就相当于把当前需要删除的结点 i 删除了么【实际删除的为结点 j 】。

    上述思路还有个问题:如果我们要删除的结点位于链表的尾部呢。那么它就没有下个结点。这样我们就需要从链表的头结点开始,进行顺序遍历并删除。

    最后还有一点:除了上面两种情况,还有个特例。就是链表中只有一个结点。上面的两种情况我们在进行删除操作时,不需要删除头结点而当只有一个结点时,我们不仅要删除函数给出的结点pToBeDeleted,同时还要将链表的头结点设置为NULL

    完整的代码如下:

      1 // deleteNode.c
      2 #include "stdio.h"
      3 #include "stdlib.h"
      4 
      5 #define N 10
      6 
      7 typedef struct Node
      8 {
      9     int m_nValue;
     10     struct Node *m_pNext;
     11 }ListNode, *pListNode;
     12 
     13 void deleteNode(ListNode **pHead, ListNode *pToBeDeleted);
     14 // 获得要删除结点
     15 ListNode *getNodeIndex(ListNode *pHead, int index);
     16 
     17 void createList(ListNode **pHead, int len)
     18 {
     19     ListNode *pTail = NULL;
     20 
     21     while(len--)
     22     {
     23         ListNode *pNew = (ListNode*)malloc(sizeof(ListNode));
     24         pNew->m_nValue = rand()%100;
     25         pNew->m_pNext = NULL;
     26 
     27         if(*pHead == NULL)
     28         {
     29             *pHead = pNew;
     30             pTail = pNew;
     31         }
     32         else
     33         {
     34             pTail->m_pNext = pNew;
     35             pTail = pNew;
     36         }
     37     }
     38 }
     39 
     40 void printList(ListNode *pHead)
     41 {
     42     while(pHead != NULL)
     43     {
     44         printf("%3d ", pHead->m_nValue);
     45         pHead = pHead->m_pNext;
     46     }
     47     printf("
    ");
     48 }
     49 
     50 void deleteNode(ListNode **pHead, ListNode *pToBeDeleted)
     51 {
     52     if(!pHead || !pToBeDeleted)
     53         return;
     54 
     55     // 链表有多个结点,并且删除的不是尾结点
     56     if(pToBeDeleted->m_pNext != NULL)
     57     {
     58         ListNode *pNext = pToBeDeleted->m_pNext;
     59         pToBeDeleted->m_nValue = pNext->m_nValue;
     60         pToBeDeleted->m_pNext = pNext->m_pNext;
     61 
     62         free(pNext);
     63         pNext = NULL;
     64     }
     65     // 还剩两种情况:要么链表只有一个结点,要么删除的是尾结点(含有多个结点)
     66     else if(*pHead == pToBeDeleted)// 链表只有一个结点
     67     {
     68         free(pToBeDeleted);
     69         pToBeDeleted = NULL;
     70         *pHead = NULL;
     71     }
     72     else // 多个结点,并且删除的是尾结点
     73     {
     74         ListNode *curNode = *pHead;
     75         while(curNode->m_pNext != pToBeDeleted) // 找到删除结点的前一个结点
     76             curNode = curNode->m_pNext;
     77 
     78         curNode->m_pNext = NULL;
     79         free(pToBeDeleted);
     80         pToBeDeleted = NULL;
     81     }
     82 }
     83 
     84 ListNode *getNodeIndex(ListNode *pHead, int index)
     85 {
     86     if(!pHead || index < 0)
     87         return NULL;
     88 
     89     ListNode *node = pHead; 
     90     while(--index)
     91         node = node->m_pNext;
     92     return node;
     93 }
     94 
     95 int main(int argc, char const *argv[])
     96 {
     97     ListNode *pHead = NULL;
     98 
     99     createList(&pHead, N);
    100     printf("Before: ");
    101     printList(pHead);
    102 
    103     ListNode *pToBeDeleted = getNodeIndex(pHead, 3);
    104     deleteNode(&pHead, pToBeDeleted);
    105     printf("After: ");
    106     printList(pHead);
    107 
    108     return 0;
    109 }
    View Code

    本文完。

  • 相关阅读:
    React组件的Refs
    Typechecking With PropTypes
    酷炫Jquery收集
    JSTL函数标签库 fn标签学习
    Struts标签 比较时间大小
    Struts2 拦截器 配置IFrame页面跳转
    实体Bean, Entity 注解设置
    Uploadify 参数说明
    Uploadify jsp使用示例
    百度umeditor
  • 原文地址:https://www.cnblogs.com/xfxu/p/4585373.html
Copyright © 2011-2022 走看看