题目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; };
->策略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; }