zoukankan      html  css  js  c++  java
  • 剑指offer之链表

    //剑指offer 之 链表
    
    //面试题6 从尾到头打印链表
    /*****************************************************************************************
    问题描述:
    输入一个链表的头节点,从尾到头反过来打印出每个节点的值
    链表节点定义如下:
    struct ListNode{
       int m_nValue;
       ListNode* m_pNext;
    };
    ******************************************************************************************/
    
    //解答如下:
    //首先实现栈 由于是用C语言 所以需要自己构建栈
    int a[100];  //用数组实现栈
    int p = 0;       //栈顶
    //入栈
    void push(int a[], int value)
    {
      if(p == 99)
      {
        /*扩大栈*/
      }
      a[p++] = value;
    }
    
    //出栈
    int pop(int a[]);
    {
      if(p <= 25)
      {
         /*压缩栈*/
      }
    
      int res = a[--p];
      return res;
    }
    
    //判断是否空栈
    int isEmpty(int a[])
    {
      return p == 0;
    }
    
    //从尾到头打印链表
    //栈实现
    void PrintListReversingly_Iteratively(ListNode* pHead)
    {
      if(pHead == NULL) return;
      ListNode *tmp = pHead;
    
      while(tmp != NULL)
      {
        push(a,tmp->value);
        tmp = tmp->m_pNext;
      }
    
      int value;
      while(!isEmpty(a))
      {
         value = pop(a);
         printf("%d-",value);
      }
    }
    
    //递归实现
    void PrintListReversingly_Recursively(ListNode* pHead)
    {
       if(pHead == NULL) return;
       if(pHead->m_pNext != NULL)
       {
        PrintListReversingly_Recursively(pHead->m_pNext);
       }
       printf("%d-",pHead->m_nValue);
       return;
    }
    
    
    
    
    
    //面试题18:删除链表的节点
    /**********************************************************
    18-1:
    在O(1)时间内删除链表节点
    给定单向链表的头指针和一个节点指针
    链表节点定义如问题6所述, 函数定义如下
    ***********************************************************/
    
    void DeleteNode(ListNode** pListNode, ListNode* pToBeDeleted)
    {
      /*
      1)节点位于中间:将下一个节点内容复制到该节点,删除下一个节点 时间复杂度为O(1)
      2)节点位于尾部:从头遍历至该节点;
      3)链表只有一个节点:删除并将头指针置为NULL
      */
      if( pListNode == NULL || pToBeDeleted == NULL) return;
      //处理情况1 
      if(pToBeDeleted->m_pNext != NULL)
      {
        ListNode *pNext = pToBeDeleted->m_pNext;
        pToBeDeleted->m_nValue = pNext->m_nValue;
        pToBeDeleted->m_pNext = pNext->m_pNext;
        free(pNext);
        pNext = NULL;
      }
      //处理情况3
      else if(*pListNode == pToBeDeleted)
      {
        free(pToBeDeleted);
        *pListNode = NULL;
        pToBeDeleted = NULL;
      }
      //处理情况2
      else 
      {
        ListNode *pNext = *pListNode;
        while(pNext->m_pNext != pToBeDeleted)
        {
          pNext = pNext->m_pNext;
        }
        pNext->m_pNext = NULL;
        free(pToBeDeleted);
        pToBeDeleted = NULL;
      }
      /*
      复杂度分析:假设链表上有n个节点
      对于前n-1个节点,时间复杂度为O(1)
      对于最后一个节点,时间复杂度为O(n)
      因此,总的为((n-1)*O(1) + O(n)) /n = O(1)
      */
    }
    
    
    /*********************************************
    18-2:
    在一个排序链表中,如何删除重复节点?
    **********************************************/
    void DeleteDuplication(ListNode** pHead)
    {
      if(pHead == NULL || *pHead == NULL) return;
    
      ListNode* pPreNode = NULL; //围绕前面节点 此节点 此节点的后续节点 操作
      ListNode* pNode = *pHead;
    
      while(pNode != NULL)
      {
        ListNode *pNext = pNode->m_pNext;
        int deleteFlag = 0;
        //判断是否需要删除操作
        if(pNode->m_nValue == pNext->m_nValue) deleteFlag = 1;
        //不需要删除,则指针向后移
        if(!deleteFlag)
        {
          pPreNode = pNode;
          pNode = pNode->m_pNext;
        }
        else
        {
          int value = pNode->m_nValue;
          ListNode *pToBeDel = pNode;
          //不断执行删除操作 从删除当前节点开始
          //前面节点用于连接后面的节点
          while(pToBeDel != NULL && pToBeDel->m_nValue == value)
          {
            pNext = pToBeDel->m_pNext;
            free(pToBeDel);
            pToBeDel = pNext;
          }
          //将需要删除的节点已删除完毕    判断之前删除的节点中是否包含头节点
          if(pPreNode == NULL) *pHead = pNext;
          else
               pPreNode->m_pNext = pNext;
    
          pNode = pNext;
        }
      }
    }
    
    
    
    
    /***********************************************************************************************
    面试题22:链表中倒数第k个节点
    输入一个链表,输出该连表中倒k个节点
    
    双指针 注意判断边界以及k小于链表个数
    ************************************************************************************************/
    ListNode* FindKthToTail(ListNode* pListNode, unsigned int k)
    {
      if(pListNode == NULL || k <= 0) return NULL;
    
      ListNode *pAhead = pListNode;
      ListNode *pBhead = NULL;
    
      for(unsigned int i=0;i<k-1;i++)
      {
         if(pAhead->m_pNext != NULL)
         {
          pAhead = pAhead->m_pNext;
         }
         else
         {
          return NULL;
         }
      }
    
     pBhead = pListNode; 
     while(pAhead != NULL)
     {
       pBhead = pBhead->m_pNext;
       pAhead = pAhead->m_pNext;
     }
    
     return pBhead;
    }
    
    
    /*
    拓展:求链表中间节点
    方法:快慢指针
    */
    
    
    /***********************************************************************************************
    面试题23: 链表中环的入口节点
    如果一个链表中有环,如何找出出环的入口节点
    解题思路:
    1)先判断有无环存在:
      设定快慢指针,若慢指针会追上快指针,则存在环
    2)得出环中节点数目n:
      在1)基础上,返回两指针相遇点,该点在环内,然后让指针从
      该点出发走一圈,记录数目
    3)根据环中节点数目n:
      还是两指针,一个在另一个前面n步,同时走,则相遇处为入口
    ************************************************************************************************/
    ListNode* MeettingNode(ListNode *pHead)
    {
      if(pHead == NULL || pHead->m_pNext == NULL) return NULL;
    
      ListNode *pFast = pHead;
      ListNode *pSlow = pHead;
    
      while(pFast != NULL && pSlow!= NULL)
      {
         if(pFast == pSlow) return pFast;
    
         pSlow = pSlow->m_pNext;
         pFast = pFast->m_pNext;  
         if(pFast != NULL)//当链表中无环时起到了判断作用
           pFast = pFast->m_pNext;
      }
      return NULL;
    }
    
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        ListNode *meetingNode = MeettingNode(pHead);
        if(meetingNode == NULL) return NULL;
    
        //得环中节点数目n
        int num_of_nodes = 1;
        ListNode *tmp1 = meetingNode;
        while(tmp1->m_pNext != meetingNode)
        {
          num_of_nodes++;
          tmp1 = tmp1->m_pNext;
        }
    
        //移动第一个指针 n步
        tmp1 = pHead;
        for(int i=0;i<num_of_nodes;i++)
        {
          tmp1 = tmp1->m_pNext;
        }
    
        //两指针一起走
        ListNode *tmp2 = pHead;
        while(tmp1 != tmp2)
        {
          tmp1 = tmp1->m_pNext;
          tmp2 = tmp2->m_pNext;
        }
    
        return tmp1;
    }
    
    
    
    
    /*****************************************************************************************
    面试题24: 反转链表
    定义一个函数,输入一个链表的头节点,
    反转该链表并输出反转后链表的头节点
    *******************************************************************************************/
    ListNode *ReverseList(ListNode* pHead)
    {
      if(pHead == NULL || pHead->m_pNext == NULL) return pHead;
    
      ListNode *pPreNode = NULL;
      ListNode *pNode = pHead;
      ListNode *pNext = NULL;
      ListNode *pReverseHead = NULL;
      while(pNode != NULL)
      {
          pNext = pNode->m_pNext;
          //判断是否到尾部
          if(pNext == NULL) pReverseHead = pNode;
    
          pNode->m_pNext = pPreNode;
          pPreNode = pNode;
          pNode = pNext;
      }
    
      return pReverseHead;
    }
    
    
    
    
    
    /*********************************************************************************************
    面试题25:合并两个排序链表
    输入两个递增排序的链表,合并这两个链表并使新链表中的
    节点仍然是排序的
    *********************************************************************************************/
    
    ListNode *Merge(ListNode *pHead1, ListNode *pHead2)
    {
       if(pHead1 == NULL) return pHead2;
       else if(pHead2 == NULL) return pHead1;
    
       ListNode* pMergedHead = NULL;
    
       if(pHead1->m_nValue < pHead2->m_nValue)
       {
           pMergedHead = pHead1;
           pMergedHead -> m_pNext = Merge(pHead1->m_pNext,pHead2);
       }
       else 
       {
           pMergedHead = pHead2;
           pMergedHead -> m_pNext = Merge(pHead1,pHead2->m_pNext);
       }
       return pMergedHead;
    }
    
    /*
    面试题35 复杂链表的复制
    实现函数ComplexListNode * Clone(ComplexListNode* pHead);
    复制一个复杂链表,每个节点除了有一个m_pNext指针指向下一个节点,还有一个
    m_pSibling指针指向链表中的任意节点orNULL
    */
    struct ComplexListNode{
      int m_nValue;
      ComplexListNode* m_pNext;
      ComplexListNode* m_pSibling;
    };
    
    /*
    1)复制每个节点链接在初始节点后面;
    2)将复制出来的节点指向SIBLING;
    3)拆分两个链表;
    */
    void CloneNodes(ComplexListNode* pHead)
    {
       if(pHead == NULL) return;
       ComplexListNode *pNode = pHead;
       while(pNode != NULL)
       {
         ComplexListNode *pClone = (ComplexListNode *)malloc(sizeof(struct ComplexListNode));
         pClone->m_nValue = pNode->m_nValue;
         pClone->m_pNext = pNode->m_pNext;
         pClone->m_pSibling = NULL;
    
         pNode->m_pNext = pClone;
         pNode = pClone->m_pNext;
       }
    }
    
    void ConnectSiblingNodes(ComplexListNode* pHead)
    {
      ListNode *pNode = pHead;
    
      while(pNode != NULL)
      {
        ComplexListNode *pClone = pNode->m_pNext;
        if(pNode->m_pSibling != NULL)
        {
           pClone->m_pSibling = pNode->m_pSibling->m_pNext;
        }
        pNode = pClone->m_pNext;
      }
    }
    
    
    ComplexListNode* ReconnectNodes(ComplexListNode* pHead)
    {
       ListNode *pNode = pHead;
       ListNode *pCloneHead - NULL;
       ListNode *pCloneNode = NULL;
    
       if(pNode != NULL)
        {
           pCloneHead = pCloneNode = pNode->m_pNext;
           pNode->m_pNext = pCloneNode->m_pNext;
           pNode = pNode->m_pNext;
        }
    
        while(pNode != NULL)
        {
           pCloneNode->m_pNext = pNode->m_pNext;
           pCloneNode = pCloneNode->m_pNext;
           pNode->m_pNext = pCloneNode->m_pNext;
           pNode = pNode->m_pNext;
        }
    
        return pCloneHead;
    }
    
    //完整步骤
    ComplexListNode* Clone(ComplexListNode* pHead)
    {
       CloneNodes(pHead);
       ConnectSiblingNodes(pHead);
       return ReconnectNodes(pHead);
    }
    
    
    
    /*
    面试题52:两个链表的第一个公共节点
    输入两个链表,找出它们的第一个公共节点
    */
    struct ListNode{
       int m_nKey;
       ListNode* m_pNext;
    };
    
    ListNode* FindFirstCommonNode(ListNode* pHead1, ListNode* pHead2)
    {
      if(pHead1 == NULL || pHead2 == NULL) return NULL;
      unsigned int nLength1 = GetListLength(pHead1);
      unsigned int nLength2 = GetListLength(pHead2);
      int nLengthDif = nLength1 - nLength2;
    
      ListNode *pListHeadLong = pHead1;
      ListNode *pListHeadShort = pHead2;
      if(nLength1 > nLength2)
      {
        nLengthDif = nLength2 - nLength1;
    
       ListNode *pListHeadLong = pHead2;
       ListNode *pListHeadShort = pHead1;
      }
    
      //先让长链表上的指针先走几步
      for(int i=0;i<nLengthDif;i++)
      {
         pListHeadLong = pListHeadLong->m_pNext;
      }
    
      //两指针同时走 直到节点值相同
      while((pListHeadLong != NULL) && (pListHeadShort != NULL) && (pListHeadLong != pListHeadShort))
      {
        pListHeadLong = pListHeadLong->m_pNext;
        pListHeadShort = pListHeadShort->m_pNext;
      }
    
      return pListHeadLong;
    }
    
    
    unsigned int GetListLength(ListNode* pHead)
    {
       unsigned int nLength = 0;
       ListNode* pNode = pHead;
       while(pNode != NULL)
       {
         nLength++;
         pNode = pNode->m_pNext;
       }
    
       return nLength;
    }
  • 相关阅读:
    如何探测浏览器是否开启js功能
    CSS3的animation功能
    registerServiceWorker创建的React项目中的registerServiceWorker作用?
    atom的react自动补全插件
    利用SQL Profiler处理开销较大的查询
    详解执行计划
    SQL Server执行计划的理解
    普通<= >=和between的sql查询方式区别与推荐
    学习如何看懂SQL Server执行计划(三)——连接查询篇
    学习如何看懂SQL Server执行计划——基本知识篇
  • 原文地址:https://www.cnblogs.com/dzy521/p/9597707.html
Copyright © 2011-2022 走看看