合併有序鏈表
思路:先比較兩個鏈表的首元結點,較小者作爲合併後鏈表的首元結點。之後遞歸比較。
注意點:1.想清楚合併的過程; 2:注意代碼的健壯性
代碼:
ListNode* Merge(ListNode* pHead1,ListNode* pHead2){
if(pHead1==NULL)
return pHead2;
if(pHead2==NULL)
return pHead1;
ListNode* MergeList=NULL;
if(pHead1->val<pHead2->val){
MergeList=pHead1;
MergeList->next=Merge(pHead1->next,pHead2);
}
else{
MergeList=pHead2;
MergeList->next=Merge(pHead1,pHead2->next);
}
return MergeList;
}
刪除指定鏈表節點
問題:O(1)時間內刪除鏈表節點
思路:常規方式時間複雜度不滿足;所以我們採取將指定節點的下一個節點覆蓋指定節點,然後修改相關指針指向。
代碼:
void DeleteNode(ListNode** pListHead,ListNode* pToBeDeleted){
if(!pListHead || !pToBeDeleted)
return;
//要刪除的節點不是尾節點
if(pToBeDeleted->next!=NULL){
ListNode* next=pToBeDeleted->next;
pToBeDeleted->val=next->val;
pToBeDeleted->next=next->next;
delete next;
next=NULL; //注意賦空,防止野指針
}
//鏈表只有一個節點,要刪除的是尾節點(頭結點)
else if(*pListHead==pToBeDeleted){
delete pToBeDeleted;
pToBeDeleted=NULL;
*pListHead=NULL;
}
//鏈表有多個節點,要刪除尾節點
else{
ListNode* pNode=*pListHead;
while(pNode->next!=pToBeDeleted){
pNode=pNode->next;
}
pNode->next=NULL;
delete pToBeDeleted;
pToBeDeleted=NULL;
}
刪除一個排序鏈表中重複的節點
思路:pPreNode保存前一個節點;pNode保存當前節點;pNext保存當前節點的下一個節點
代碼:
//有可能刪除頭結點
void Delete(ListNode **pHead){
if(pHead==NULL || *pHead==NULL)
return;
ListNode pPreNode=NULL;
ListNode pNode=*pHead;
while(pNode!=NULL){
ListNode *pNext=pNode->next;
bool flag=0;
if(pNext!=NULL && pNode->val==pNext->val)
flag=1;
if(!flag){
pPreNode=pNode;
pNode=pNode->next;
}
else{
int val=pNode->val;
ListNode *ToBeDel=pNode;
while(ToBeDel!=NULL && ToBeDel->val=val){
pNext=ToBeDel->next;
delete ToBeVal;
ToBeDel=NULL;
ToBeDel=pNext;
}
//如果第一個節點被刪除了
if(pPreNode==NULL){
*pHead=pNext;
}
else{
pPreNode->next=pNext;
}
pNode=pNext;
}
}
}
鏈表中環的入口節點
問題:如果一個鏈表中包含環,如何找出環的入口節點?
思路:
第一步:如何確定一個鏈表中包含環?
定義兩個指針,同時從鏈表的頭結點出發,一個指針一次走一步,另一個指針一次走兩步。如果走的快的指針追上了走的慢的指針,那麼就包含環;如果走的快的指針走到了鏈表的額末尾都沒有追上另外一個指針,那麼就不包含環。
第二步:如何找到環的入口?
P1,P2都指向鏈表的首元節點。如果環中有n個節點,那麼P1先走n步,然後兩個指針一起走直到相遇。相遇的節點就是入口節點。
那麼環的節點數目又從何得知?
之前判斷是否存在環的時候,追上時候的節點一定是在環中。我們可以一邊計數,一邊從這個位置一直移動,直到下一次回到這個節點。
代碼:
//存在環的前提下,找到兩個指針的相遇位置
ListNode* MeetingNode(ListNode* pHead){
if(pHead==NULL)
return;
ListNode* pSlow=pHead->next;
if(pSlow==NULL)
return;
ListNode* pFast=pSlow->next;
while(pSlow!=NULL && pFast!=NULL){
if(pFast==pSlow)
return pFast;
pSlow=pSlow->next;
pFast=pFast->next;
if(pFast!=NULL)
pFast=pFast->next;
}
return NULL;
}
ListNode* EntryNodeOfLoop(ListNode* pHead){
ListNode* meetingnode=MeetingNode(pHead);
if(MeetingNode==NULL)
return NULL;
//獲取環中節點的數目
int len=0;
ListNode* pNode1=meetingnode;
while(pNode1->next!=meetingnode){
pNode1=pNode1->next;
++len
}
//先移動pNode1 len 次數
pNode1=pHead;
for(int i=0;i<len;++i){
pNode1=pNode1->next;
}
//再移動pNode1和pNode2
ListNode* pNode2=pHead;
while(pNode1!=pNode2){
pNode1=pNode1->next;
pNode2=pNode2->next;
}
return pNode1;
}
反轉鏈表
思路:我們需要三個指針,分別指向遍歷到的節點和它的前一個節點和後一個節點
代碼:
ListNode* ReverseList(ListNode* pHead){
ListNode* pReverseHead=NULL;
ListNode* pNode=pHead;
ListNode* pPreNode=NULL;
while(pNode!=NULL){
ListNode* pNext=pNode->next;
if(pNext==NULL){
pReverseHead=pNode;
}
pNode->next=pPreNode;
pPreNode=pNode;
pNode=pNext;
}
return pReverseHead;
}
鏈表中倒數第K個節點
思路一:
遍歷兩次鏈表。第一次得出鏈表節點數目n。第二次走n-k+1步得到倒數第K個節點。
思路二:
要求只遍歷一次的話。需要用到兩個指針。第一個指針先走k-1步,然後兩個指針開始一起走,當第一個指針走到尾節點時,第二個指針正好指向倒數第K個節點。
代碼:
//思路二實現
//注意代碼的健壯性: k的合法性
ListNode* Find(ListNode *pHead){
if(pHead==nullptr || k==0)
return;
ListNode* pNode1=pHead;
ListNode* pNode2=pHead;
for(int i=0;i<k-1;++i){
//k的合法性
if(pNode1->next!=NULL)
pNode1=pNode1->next;
else
return NULL;
}
while(pNode1->next!=NULL){
pNode1=pNode1->next;
pNode2=pNode2->next;
}
return pNode2;
}
兩個鏈表中的第一個公共節點
問題:輸入兩個鏈表,找出它們的第一個公共節點。
思路:長鏈表先走長度差步數,然後同時遍歷。
代碼:
Node* FindFirstCommonNode(Node* l1,Node* l2){
int len1=getlength(l1);
int len2=getlength(l2);
Node* short=l1;
Node* long=l2;
int len=len2-len1;
if(len1>len2){
short=l2;
long=l1;
len=len1-len2;
}
for(int i=0;i<len;++i){
l2=l2->next;
}
while(l1!=nullptr && l2!=nullptr && l1!=l2){
l1=l1->next;
l2=l2->next;
}
Node* first=l1;
return l1;
}
複雜鏈表的複製
問題:複製一個複雜鏈表。每個節點除了有一個m_pNext指針指向下一個節點,還有一個m_pSibling指針指向鏈表中的任意幾點或者nullptr。
思路:
ComplexListNode* Clone(ComplexListNode* pHead)
{
CloneNodes(pHead);
ConnectSiblingNodes(pHead);
return ReconnectNodes(pHead);
}
1.複製任意節點N並創建新節點N’,再把N’鏈接到N的後邊
2.設置複製出來的節點的m_pSibling
3.拆分爲兩個鏈表:奇數節點組成的就是原鏈表,偶數部分組成的就是複製鏈表
代碼:
void CloneNodes(ComplexListNode* pHead){
ComplexListNode* Node=pHead;
while(Node!=NULL){
ComplexListNode* pCloned=new ComplexListNode();
pCloned->m_pNext=Node->m_pNext;
pCloned->m_nValue=Node->m_nValue;
pCloned->m_pSibling=nullptr;
Node->m_pNext=pCloned;
Node=Node->m_pNext;
}
}
void ConnectSiblingNodes(ComplexListNode* pHead){
ComplexListNode* Node=pHead;
while(Node != nullptr){
ComplexListNode* pCloned=Node->next;
if(Node->m_pSibling!=nullptr){
pCloned->m_pSibling=Node->m_pSibling->m_pNext;
}
Node=pCloned->m_pNext;
}
}
ComplexListNode* ReconnectNodes(ComplexListNode* pHead){
//細節處理:
//先創建一個節點接收原鏈表的頭結點方便之後的遍歷操作;
//跟着創建新鏈表的頭;
//跟着創建一個pClonedNode方便之後的新鏈表的遍歷
ComplexListNode* Node=pHead;
ComplexListNode* pClonedHead=nullptr;
ComplexListNode* pCloneNode=nullptr;
if(Node!=nullptr){
pClonedNode=pClonedHead=Node->m_pNext;
Node->m_pNext=pClonedNode->m_pNext;
Node=Node->m_pNext;
}
while(Node!=nullptr){
pClonedNode->m_pNext=pNode->m_pNext;
pClonedNode=pClonedNode->m_pNext;
Node->m_pNext=pClonedNode->m_pNext;
Node=Node->m_pNext;
}
return pCloneHead;
}