【绝知此事要躬行】线性表之链表OJ(下)
線性表之鏈表OJ(下)
“我不唱聲嘶力竭的情歌,不表示沒(méi)有心碎的時(shí)刻”——《孤獨(dú)患者》
經(jīng)過(guò)上期鏈表OJ(上)
相信大家也都摩拳擦掌準(zhǔn)備迎接這期難度稍高的鏈表OJ下啦,沖沖沖!
1.鏈表分割(牛客)
題目鏈接 :CM11 鏈表分割
題目描述:
現(xiàn)有一鏈表的頭指針1ListNode* pHead,給一定值x,編寫一段代碼將所有小于x的結(jié)點(diǎn)排在其余結(jié)點(diǎn)之前,且不能改變?cè)瓉?lái)的數(shù)據(jù)順序,返回重新排列后的鏈表的頭指針。
思路:類似于上期合并兩個(gè)有序鏈表的思路,這次我們要用兩個(gè)鏈表,為了簡(jiǎn)化我們的邏輯,使用帶哨兵的鏈表。
1. 遍歷原鏈表,一個(gè)存放值小于x的節(jié)點(diǎn),另一個(gè)存值大于等于x的節(jié)點(diǎn)。
2. 將兩個(gè)鏈表連接起來(lái)
3. 記錄要返回的頭,釋放哨兵
圖示:
代碼實(shí)現(xiàn):
class Partition { public:ListNode* partition(ListNode* pHead, int x) {ListNode* LessHead=new ListNode(0);ListNode* GreaterHead=new ListNode(0);ListNode *LessTail=LessHead, *GreaterTail=GreaterHead;for(ListNode* cur=pHead;cur;cur=cur->next){if(cur->val<x){LessTail->next=cur;LessTail=cur;}else{GreaterTail->next=cur;GreaterTail=cur;}}LessTail->next=GreaterHead->next;GreaterTail->next=NULL;ListNode* head=LessHead->next;delete LessHead;delete GreaterHead;return head;} };2.鏈表的回文結(jié)構(gòu)
題目鏈接:鏈表的回文結(jié)構(gòu)
題目描述:
對(duì)于一個(gè)鏈表,請(qǐng)?jiān)O(shè)計(jì)一個(gè)時(shí)間復(fù)雜度為O(n),額外空間復(fù)雜度為O(1)的算法,判斷其是否為回文結(jié)構(gòu)。
給定一個(gè)鏈表的頭指針A,請(qǐng)返回一個(gè)bool值,代表其是否為回文結(jié)構(gòu)。保證鏈表長(zhǎng)度小于等于900。
我們遇到的困難主要是這是個(gè)單向的鏈表。沒(méi)法向前訪問(wèn)節(jié)點(diǎn),需要能“逆序”
思路一:
圖示:
代碼實(shí)現(xiàn):
//返回一個(gè)鏈表的中間節(jié)點(diǎn) //快慢節(jié)點(diǎn) ListNode* middleNode(ListNode *head) {ListNode *slow,*fast;fast=slow=head;while(fast&&fast->next){fast=fast->next->next;slow=slow->next;}return slow; } //逆置鏈表,返回新的頭節(jié)點(diǎn) // 1.三個(gè)指針?lè)D(zhuǎn)鏈表 ListNode* reverseList1(ListNode* head) {ListNode *prev=NULL,*cur=head;while(cur){ListNode* next=cur->next;cur->next=prev;prev=cur;cur=next;}return prev; } //2.頭插法翻轉(zhuǎn)鏈表 ListNode* reverseList2(ListNode* head) {ListNode* newHead=NULL;ListNode* cur=head;while(cur){ListNode *next=cur->next;cur->next=newHead;newHead=cur;cur=next;}return newHead; } class PalindromeList { public:bool chkPalindrome(ListNode* A) {//獲得中間節(jié)點(diǎn)ListNode* middle=middleNode(A);//將中間節(jié)點(diǎn)開始往后的鏈表逆置//ListNode* rHead=reverseList1(middle);ListNode* rHead=reverseList2(middle);while(A&&rHead){if(A->val==rHead->val){A=A->next;rHead=rHead->next;}elsereturn false;}return true;} };!!!不推薦這種寫法!!!
因?yàn)檫@種寫法改變了傳入鏈表的結(jié)構(gòu),而我們這邊的需求只是判斷一個(gè)鏈表是否具有回文結(jié)構(gòu)。
思路二(利用棧實(shí)現(xiàn)):
1. 找中間節(jié)點(diǎn)
2. 把頭至中間節(jié)點(diǎn)(不包括)入棧
3. 從中間節(jié)點(diǎn)開始,棧頂元素->val和cur->val相等就出棧
4. 最后判斷棧是否為空
代碼實(shí)現(xiàn):
class PalindromeList { public:bool chkPalindrome(ListNode* A){ListNode *fast,*slow,*middle,*cur;fast=slow=A;while(fast&&fast->next){fast=fast->next->next;slow=slow->next;}middle=slow;stack<ListNode*> st;for(cur=A;cur!=middle;cur=cur->next)st.push(cur);for(cur=middle;cur;cur=cur->next){if(st.top()->val==cur->val)st.pop();}if(st.empty())return true;return false;} };3.相交鏈表
題目鏈接:160. 相交鏈表
題目描述:
給你兩個(gè)單鏈表的頭節(jié)點(diǎn) headA 和headB ,請(qǐng)你找出并返回兩個(gè)單鏈表相交的起始節(jié)點(diǎn)。如果兩個(gè)鏈表不存在相交節(jié)點(diǎn),返回null 。
圖示兩個(gè)鏈表在節(jié)點(diǎn)c1 開始相交:
來(lái)源:力扣(LeetCode)
思路:
要判斷鏈表是否相交比較簡(jiǎn)單,兩個(gè)鏈表都走到尾,看最后一個(gè)節(jié)點(diǎn)是否相同即可(因?yàn)殒湵硐嘟灰欢ㄊ沁@種倒著的Y型,而不可能是X型)
我們還需要返回第一個(gè)相交節(jié)點(diǎn),如果兩個(gè)鏈表長(zhǎng)度相同,一起遍歷,如果節(jié)點(diǎn)相同直接返回即可。
在長(zhǎng)度不同的情況下,長(zhǎng)的先走gap步,再一起走即可
代碼實(shí)現(xiàn):
class Solution { public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {ListNode *pa=headA,*pb=headB;int lenA=1,lenB=1;while(pa->next){lenA++;pa=pa->next;}while(pb->next){lenB++;pb=pb->next;}if(pa!=pb)return NULL;ListNode *shortList=headA,*longList=headB;if(lenA>lenB){shortList=headB;longList=headA;}int gap=abs(lenA-lenB);while(gap--)longList=longList->next;while(shortList&&longList){if(shortList==longList)return longList;longList=longList->next;shortList=shortList->next;}return NULL;} };4.復(fù)制帶隨機(jī)指針的鏈表
題目鏈接:138. 復(fù)制帶隨機(jī)指針的鏈表
題目描述:
給你一個(gè)長(zhǎng)度為 n的鏈表,每個(gè)節(jié)點(diǎn)包含一個(gè)額外增加的隨機(jī)指針 random ,該指針可以指向鏈表中的任何節(jié)點(diǎn)或空節(jié)點(diǎn)。
構(gòu)造這個(gè)鏈表的 深拷貝。 深拷貝應(yīng)該正好由 n個(gè) 全新 節(jié)點(diǎn)組成,其中每個(gè)新節(jié)點(diǎn)的值都設(shè)為其對(duì)應(yīng)的原節(jié)點(diǎn)的值。新節(jié)點(diǎn)的 next 指針和 random指針也都應(yīng)指向復(fù)制鏈表中的新節(jié)點(diǎn),并使原鏈表和復(fù)制鏈表中的這些指針能夠表示相同的鏈表狀態(tài)。復(fù)制鏈表中的指針都不應(yīng)指向原鏈表中的節(jié)點(diǎn) 。
例如,如果原鏈表中有 X 和Y 兩個(gè)節(jié)點(diǎn),其中 X.random --> Y 。那么在復(fù)制鏈表中對(duì)應(yīng)的兩個(gè)節(jié)點(diǎn) x 和 y ,同樣有x.random --> y 。
返回復(fù)制鏈表的頭節(jié)點(diǎn)。
來(lái)源:力扣(LeetCode)
示意圖(源自力扣):
這題是本blog中難度最高的一題,無(wú)論是從思路上還是代碼的控制上都有一定的挑戰(zhàn)性
首先我們要理解深拷貝
這是C++中和淺拷貝相對(duì)的一個(gè)概念。這邊我們不過(guò)多深入的討論(后續(xù)我會(huì)在C++專題中深入討論)
此處可以理解為,我們?cè)賰?nèi)存中自己申請(qǐng)節(jié)點(diǎn),再按照這個(gè)鏈表連接的樣子,來(lái)把這些節(jié)點(diǎn)連接起來(lái)
“如果原鏈表中有 X 和Y 兩個(gè)節(jié)點(diǎn),其中 X.random --> Y 。那么在復(fù)制鏈表中對(duì)應(yīng)的兩個(gè)節(jié)點(diǎn) x 和 y ,同樣有x.random --> y ”
是一個(gè)**“照貓畫虎“**的過(guò)程。
分析:
如果不含random指針,這個(gè)過(guò)程是很簡(jiǎn)單的。遍歷原鏈表,復(fù)制一個(gè)當(dāng)前節(jié)點(diǎn),按順序建立一個(gè)新表即可。
但有了random指針,問(wèn)題就復(fù)雜起來(lái)了。
主要在于我們?cè)谠碇?#xff0c;通過(guò)random指針可以知道某個(gè)節(jié)點(diǎn)的random指針指向哪個(gè)節(jié)點(diǎn),但是在建立新表的過(guò)程中,我們并找不到新表的random指針該指向哪個(gè)新表中的節(jié)點(diǎn)(即新表和原表節(jié)點(diǎn)之間并無(wú)聯(lián)系)
這個(gè)時(shí)候我們要解決這個(gè)問(wèn)題,必須在原表與新表之間建立一種聯(lián)系
我們采取這樣一種做法:
把自己申請(qǐng)的拷貝節(jié)點(diǎn)連接在原節(jié)點(diǎn)的后面。
這樣通過(guò)random指針找到的原節(jié)點(diǎn)的后一個(gè)就是新表中對(duì)應(yīng)拷貝節(jié)點(diǎn)的random指針指向的拷貝節(jié)點(diǎn)。
圖像解析:
解法:
1. 將拷貝節(jié)點(diǎn)連接到原節(jié)點(diǎn)后面
2. 處理拷貝節(jié)點(diǎn)的random指針
3. 將拷貝節(jié)點(diǎn)在原鏈表上”剪“下來(lái)并連接形成新表
代碼實(shí)現(xiàn):
typedef struct Node Node; struct Node* copyRandomList(struct Node* head) {//1.將拷貝節(jié)點(diǎn)連接到原節(jié)點(diǎn)后面Node* cur=head;while(cur){Node* next=cur->next;Node* copy=(Node*)malloc(sizeof(Node));copy->val=cur->val;copy->next=next;cur->next=copy;cur=next;}//2.處理拷貝節(jié)點(diǎn)的random指針cur=head;while(cur){Node* copy=cur->next;if(!(cur->random))copy->random=NULL;elsecopy->random=cur->random->next;cur=copy->next;}//3.將拷貝節(jié)點(diǎn)剪下來(lái)并連接cur=head;Node *copyhead,*copytail;copyhead=copytail=NULL;while(cur){Node* copy=cur->next;if(copyhead==NULL)copyhead=copy;else copytail->next=copy;copytail=copy;cur=cur->next=copy->next;}return copyhead; }🤗到此我們這博客也接近尾聲啦。
😊希望大家能在閱讀完后有所收獲!!!這將是對(duì)我最大的激勵(lì)!
敲代碼,碼字,作圖不易,期待一個(gè)小小的點(diǎn)贊??
如果你對(duì)博客內(nèi)容有什么疑惑,或者對(duì)思考題有什么不解,很歡迎大家來(lái)和我交流討論哦?
本期所有的代碼我將傳到我的gitee倉(cāng)庫(kù)中,如有需要可自行下載
倉(cāng)庫(kù)傳送門:數(shù)據(jù)結(jié)構(gòu)
我們下篇博客見!😘
總結(jié)
以上是生活随笔為你收集整理的【绝知此事要躬行】线性表之链表OJ(下)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 《致青春》
- 下一篇: Keep It Mac版