數據結構學習——鏈表相關習題(持續補充...)

合併有序鏈表

思路:先比較兩個鏈表的首元結點,較小者作爲合併後鏈表的首元結點。之後遞歸比較。

注意點: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;

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章