算法——链表

题目1:输入两个链表,找出它们的第一个公共节点。链表节点定义如下:
struct ListNode
{
     int m_nKey;
     ListNode* m_pNext;
};
思路:有公共节点的链表一定是在尾部有若干个相同的节点,两条链表呈Y状。
->求两条链表的长度差diffLen。然后使用两个指针分别指向两条链表表头,长链表指针先走diffLen步,然后两个指针再同步向前。 
 
ListNode* FirstCommonNode(ListNode *pChain1,ListNode* pChain2){
     ListNode* firstCommonNode=nullptr;
     if(pChain1!=nullptr && pChain2!=nullptr) {
          int len1=GetChainLength(pChain1);
          int len2=GetChainLength(pChain2);
          int diffLen=len1-len2;
          ListNode *pLong=pChain1, *pShort=pChain2;
          if(len1<len2){
               pShort=pChain1;
               pLong=pChain2;    
               diffLen=len2-len1;
          }
          for(int i=0;i<diffLen;i++)
               pLong=pLong->m_pNext;
          while(pLong!=nullptr && pLong!=pShort){
               pLong=pLong-<m_pNext;
               pShort=pLShort->m_pNext;
          }
          firstCommonNode=pLong;
     }
     return firstCommonNode;     
}
int GetChainLength(ListNode *pHead){
     int length=0;
     while(pHead!=nullptr){
          pHead=pHead->m_pNext;
          length++; 
     }
     return length;
}

 


题目2:请实现函数ComplexListNode* Clone(ComplexListNode* pHead),复制一个复杂链表。在复杂链表中,每个节点除有一个m_pNext指针指向下一个节点,还有一个m_pSibling指针指向链表中的任意节点或者nullptr。节点的C++定义如下。
struct ComplexListNode
{
     int m_nValue;
     ComplexListNode* m_pNext;
     ComplexListNode* m_pSlibling;
};
 
思路:问题的难点在于无法根据原链表中的m_pSlibling确定复制链表中的m_pSlibling的值。但第一步总是先复制整条链表,建立p_Next的关系。
->策略一:使用一个Hash表来建立原/复制链表再相同位置节点之间的对应关系。
->策略二:第一步复制链表时,将新申请的节点插入到原链表对应节点的后面。然后及可以根据原链表节点m_pSlibling确定复制后的m_pSlibling值。最后再根据奇偶位置拆分。
 
//C++ Code
ComplexListNode* Clone(ComplexListNode* pHead){
     ComplexListNode* pCloneHead=nullptr;
     ComplexListNode* pCur=pHead;
     //Step1
     while(pCur!=nullptr){
          ComplexListNode* newNode=new ComplexListNode();
          newNode->m_Value=pCur->m_Value;
          newNode->m_pNext=pCur->m_pNext;
          newNode->m_pSlibing=nullptr;
          pCur->m_pNext=newNode;
          pCur=pCur->m_pNext;
     }
     //Step2
     pCur=pHead;
     ComplexListNode* pCloneCur=pHead;
     while(pCur!=nullptr){
          pCloneCur=pCur->m_pNext;
          if(pCur->m_pSLibling!=nullptr)
               pCloneCur->m_pSlibling=pCur->m_pSlibling->m_pNext;
          pCur=pCur->m_pNext; 
     }
     //Step3
     if(pHead!=nullptr){
          pCloneHead=pHead->m_pNext;
          pCur->m_pNext=pCloneHead->m_pNext;
          pCur=pCur->m_pNext;
          pCloneCur=pCloneHead;
     }
     while(pCur!=nullptr){
         pCur->m_pNext=pCloneCur->m_pNext; 
         pCur=pCur->m_pNext;
         pCloneCur->m_pNext=pCur->m_pNext;               
         pCloneCur=pCloneCur->m_pNext;
     }
     return pCloneHead;
}
 

题目3:给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间内删除该节点。链表节点与函数的定义如下:
 
struct ListNode{
     int m_nValue;
     ListNode* m_pNext;
}
思路:本题的难点在于时间复杂度的要求。如果先扫描定位链表前一个节点的话则需要O(n)的时间复杂度。
->策略:可以将下一链表节点的数据复制给本链表,然后删除下一节点。
->边界条件:下一节点为空,或删除节点为头结点。
 
void DeleteNode(ListNode* pHead,ListNode* pDelete){
     if(pHead!=nullptr && pDelete!=nullptr){
          bool headFlag=pHead==pDelete ? true :false;
          if(pDelete->m_pNext!=nullptr){
               pTemp=pDelete->m_pNext;
               pDelete->m_pNext=pTemp->m_pNext;
               pDelete->m_nValue=pTemp->m_nValue;
               delete pTemp; 
          }
          ListNode* pNode=pHead;
          while(pNode!=nullptr && pNode->m_pNext!=pDelete)
               pNode=pNode->m_pNext;
          if(pNode!=nullptr){
              pNode->m_pNext=nullptr;
              delete pDelete; 
              pDelete=nullptr;
          }
          if(headFlag) pHead=pDelete;    
     }
     return;
}
 

题目4:输入一个链表,输出该链表的倒数第k个节点。
思路:双指针,先拉开k步长的距离,然后同步向前。需要注意的是无效输入的验证,以及边界测试。
 
ListNode* FindKthToTail(ListNode* pListNode,unsigned int k)
{
     ListNode* pResult=nullptr;
     if(pListNode!=0 && k>0){
           ListNode* pAhead=pHead;
           ListNode* pBehind=nullptr;
           while(pAhead!=nullptr && k>0){
               pAhead=pAhead->m_pNext;
               k--;
           }
           if(k<0){
               pBehind=pHead;
               while(pAhead!=nullptr){
                   pAhead=pAhead->m_pNext;
                   pBehind=pBehind->m_pNext; 
               }
           } 
     }
     return pResult;
}
 

题目5:如果一个链表中包含环,如何找出环的入口节点?
思路:快慢指针相遇能证明有环;从相遇节点开始,慢指针绕环一圈可以得到环的大小;设置前后指针,相差环大小的步长,遍历的相遇点即为环的入口节点。
 

题目6:定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头接点。链表的定义如下:
struct ListNode
{
     int m_nKey;
     ListNode* m_pNext;
};
思路:时间复杂度为O(1)。
->策略1:使用辅助栈,从头遍历链表,依次将链表指针进栈,全部进栈后,依次弹出、链接。
->策略2:边遍历边链接。
 
ListNode* RevertList(ListNode *pHead){
     ListNode* pRevertHead=nullptr;
     ListNode* pCur=pHead;
     while(pCur!=nullptr){
          ListNOde *pNext=pCur->m_pNext;
          pCur->m_pNext=pRevertHead;
          pRevertHead=pCur;
          pCur=pNext;
     }
     return pRevertHead;
}

猜你喜欢

转载自www.cnblogs.com/enchel/p/9272535.html
今日推荐