日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

链表经典题

發布時間:2025/3/15 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 链表经典题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目

序號題目難度鏈接外鏈方法
1.刪除所有結點簡單跳轉LeetCode雙指針
2.反轉鏈表簡單跳轉LeetCode雙指針or取頭構造
3.取鏈表中間結點簡單跳轉LeetCode快慢指針
4.取鏈表倒數結點簡單跳轉牛客雙指針
5.合并鏈表簡單跳轉LeetCode插入
6.分割鏈表簡單跳轉牛客插入
7.鏈表回文結構簡單跳轉牛客雙指針+逆置
8.相交鏈表簡單跳轉LeetCode消除差距一起走
9.環狀鏈表簡單跳轉LeetCode雙指針+追蹤
10.環狀鏈表2中等跳轉LeetCode斷環+相交鏈表
11.復雜鏈表的復制中等跳轉LeetCode錯位復制
12.鏈表的插入排序中等跳轉LeetCode哨兵結點+插入
13.鏈表移除重復元素中等跳轉牛客三指針

一:刪除結點中給定值的所有結點


法一:構建頭結點法
此題所給出的是一個沒有頭結點的鏈表,沒有頭結點的鏈表在操作時對于第一個結點的處理非常苦難,要考慮很多種情況,所以我們可以自己造一個頭結點,最終返回的時候再head放到該放到的位置上。這種方法其實有點作弊的嫌疑,但是無疑是一種非常好的方法

/*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/struct ListNode* removeElements(struct ListNode* head, int val) {struct ListNode* dummy=(struct ListNode*)malloc(sizeof(struct ListNode));//制造頭結點dummy->next=head;//頭結點下一個就是第一個元素dummy->val=NULL;//頭結點不含數據元素struct ListNode* pre=dummy;struct ListNode* cur=head;//雙指針法while(cur!=NULL){if(cur->val!=val){cur=cur->next;pre=pre->next;}else{pre->next=cur->next;cur=cur->next;}}head=dummy->next;//一定要把頭指針找回free(dummy);return head; }

法二:迭代法
這種方法要注意一點,由于pre指針在初始狀態下是空,cur指針首先指向head,所以如果第一個結點就是要刪除的結點,那么進行刪除操作時“pre->next=cur->next”的操作顯然就是有問題的,所以說對于這種情況要特殊處理

struct ListNode* removeElements(struct ListNode* head, int val) {struct ListNode* pre=NULL;struct ListNode* cur=head;while(cur!=NULL)//cur進行掃描{if(head->val==val)//特殊情況,第一個結點就是要刪除的結點{head=cur->next;//head向后走free(cur);cur=head;//一定注意這一點,因為沒有這句cur就成了野指針了}else//一般情況{if(cur->val==val)//所指結點就是要刪除的結點{pre->next=cur->next;free(cur);cur=pre;//一定注意這一點,因為沒有這句cur就成了野指針了}else//不是就迭代{pre=cur;cur=cur->next;}}}return head; }


二:反轉單鏈表


法一:迭代法
使用迭代的方法逆置鏈表,需要用到三個指針

struct ListNode* reverseList(struct ListNode* head) {//遞歸/*if (head == NULL || head->next == NULL) {return head;}struct ListNode* newHead = reverseList(head->next);head->next->next = head;head->next = NULL;return newHead;*/struct ListNode* pre=head;struct ListNode* cur=NULL;struct ListNode* save;while(pre!=NULL){save=pre;pre=pre->next;save->next=cur;cur=save;}return cur; }

法二:頭插法
把原來的結點取下來,然后進行頭插

struct ListNode* newHead=NULL;struct ListNode* cur=head;while(cur != NULL){head=cur;cur=cur->next;head->next=newHead;newHead=head;}return newHead;


三:取鏈表的中間結點


法一:普通解法
這種方法是最容易想到的方法,首先遍歷鏈表,得到總的結點數,然后找出中間節點,再次遍歷即可。

int count=0;struct ListNode* cur=head;while(cur!=NULL){count++;cur=cur->next;}int ret=count/2;cur=head;while(ret){cur=cur->next;ret--;}return cur;

法二:雙指針法
一般來說,遍歷一次或者稍加巧妙的方法,就可以用到雙指針。定義一個慢指針和快指針,快指針每次走兩步,慢指針每次走一步,最后慢指針所指就是中間節點

struct ListNode* slow=head;struct ListNode* fast=head;while(fast->next!=NULL){fast=fast->next->next;slow=slow->next;if(fast==NULL)//這一點注意:對于偶數個結點,最后一次fast為NULL,如果不跳出,繼續判斷的話會出錯break;}return slow;


四:取鏈表倒數第K個結點


此題可以用樸素解法,也可以將鏈表導致,求其第k個結點。但更為好的方法還是雙指針法

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {struct ListNode* slow=pListHead;struct ListNode* fast=pListHead;while(k--){if(fast)//注意有一種非常特殊的情況就是,鏈表有5個結點但是讓你求倒數第8個結點,這種情況會出現內存錯誤fast=fast->next;elsereturn NULL;}while(fast!=NULL){slow=slow->next;fast=fast->next;}return slow; }


五:合并鏈表


思路不難,創建新的鏈表,然后分別遍歷兩個原鏈表,把小的插入

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {struct ListNode* cur1=l1;struct ListNode* cur2=l2;struct ListNode* l3=(struct ListNode*)malloc(sizeof(struct ListNode));l3=NULL;struct ListNode* cur3=l3;if(cur1==NULL && cur2!=NULL)//如果第一個空,第二個不空{return cur2;}if(cur1!=NULL && cur2==NULL)//如果第二個空,第一個不空{return cur1;}while(cur1!=NULL && cur2!=NULL){if(cur2->val<cur1->val){if(l3==NULL)//特殊處理頭結點{l3=cur2;cur3=l3;cur2=cur2->next;}else{cur3->next=cur2;cur2=cur2->next;cur3=cur3->next;}}else{if(l3==NULL)//特殊處理頭結點{l3=cur1;cur3=l3;cur1=cur1->next;}else{cur3->next=cur1;cur1=cur1->next;cur3=cur3->next;}}}if(cur3==NULL)//l1和l2同時是空鏈表{return cur3;}if(cur1==NULL)//cur1等于NULL了說明,說明l2長于l1{cur3->next=cur2;}if(cur2==NULL)//cur2等于NULL了說明,說明l1長于l2{cur3->next=cur1;}return l3; }


六:分割鏈表


此題要注意,要保持相對順序不變,比如4->3->2->1,要把所有小于3的結點放在其余結點之前,那么可以是2->1->4->3,但是決不能是1->2->4->3或2->1->3->4等。

這個題類似于上述合并鏈表,選定題目要求的元素,遍歷此鏈表,小于此元素的尾插到一個鏈表,大于的則尾插到另一個鏈表。但是尾插時會有一個問題,就是新創造的頭指針開始為NULL,尾插時會出現NULL錯誤,所以為了避免這種問題,我們可以創造一個“哨兵”結點,該哨兵結點并不存儲任何有效數據,但是它能使尾插的代碼統一變為"head->next==Newhead"。

class Partition { public:ListNode* partition(ListNode* pHead, int x) {struct ListNode*small_tail=NULL;//小的尾節點struct ListNode*small_guard=(ListNode*)malloc(sizeof(ListNode));//小的哨兵結點small_guard->next=NULL;small_tail=small_guard;struct ListNode*large_tail=NULL;//大的尾節點struct ListNode*large_guard=(ListNode*)malloc(sizeof(ListNode));//大的哨兵結點large_guard->next=NULL;large_tail=large_guard;struct ListNode* cur=pHead;//遍歷指針while(cur){if(cur->val<x)//小于x進行samll鏈表的尾插{small_tail->next=cur;small_tail=small_tail->next;cur=cur->next; }else//大于x進行large鏈表的尾插{large_tail->next=cur;large_tail=large_tail->next;cur=cur->next;}}small_tail->next=large_guard->next;//小的后面連上的大的large_tail->next=NULL;//注意收尾return small_guard->next;} };


七:鏈表的回文結構


所謂回文數,就是指正讀和反讀一樣的數字,比如1221。
題目中對于空間復雜度和時間復雜度都有限制,所以就不能使用建立數組的方式進行比較,即使沒有這種限制,采用此方法也是不可取的,因為一旦鏈表過長,時間復雜度將會很大。
我們的思路是,利用之前講過的雙指針法找到中間節點,以中間節點為新的鏈表,對后半部分進行逆置操作,然后進行比較
這里有幾點需要說明

ListNode* reverse(ListNode* head)//逆置 {ListNode* newHead=NULL;ListNode* cur=head;while(cur){head=cur;cur=cur->next;head->next=newHead;newHead=head;}return newHead; }class PalindromeList{ public:bool chkPalindrome(ListNode* A){ListNode* slow=A;ListNode* fast=A;ListNode* pre=NULL;while(fast->next){pre=slow;fast=fast->next->next;slow=slow->next;if(fast==NULL){break;}}pre->next=NULL;//斷開slow=reverse(slow);//逆置,此時slow指向“原鏈表最后一個”while(A)//夾逼比較{if(A->val!=slow->val)//一旦出現不相等,一定不相等{return false;}else{A=A->next;slow=slow->next;}} return true;//要是可以執行到這里,那么肯定是回文結構} };


八:相交鏈表


此題最大的障礙就是,兩個鏈表的長度可能不一致,這樣一來就無法比較了。所以可以先分別求出兩個鏈表的長度,接著計算出他們的差距,讓長鏈表先走完這段差距,這樣一樣短的和長的就能同時開始向后走了,一旦出現某個位置相同,那么此位置必然是相交位置

注意下面對于長短鏈表的處理,如果硬要判斷出A長還是B長,那么就要進行分類。所以可以讓一個變量longlist始終保存最長的那個鏈表的地址(也就是先假設再調整)。

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {int length_A=0;int length_B=0;int gap=0;struct ListNode* curA=headA;struct ListNode* curB=headB;while(curA){++length_A;curA=curA->next;}while(curB){++length_B;curB=curB->next;}struct ListNode* longlist=headA;//注意以下處理struct ListNode* shortlist=headB;if(length_B>length_A){longlist=headB;shortlist=headA;}gap=abs(length_A-length_B);while(gap--)//先讓長的走完差距{longlist=longlist->next;}while(longlist){if(longlist==shortlist)return longlist;longlist=longlist->next;shortlist=shortlist->next;}return NULL; }

九:環狀鏈表


可以用快慢指針,讓快指針先走(每次2步),慢指針后走(每次1步),當兩個指針都進入環內時,經過一定次數,一定有快慢指針相等,也就證明有環

也就是此題邏輯為,如果fast某一刻為NULL了,則一定沒有環,但是如果有環,那么fast和slow一定能相遇,從而判斷出來

這里還需要特別注意,快指針必須每次2步,因為如果其他步數,就會存在覆蓋不全的情況,或者說相遇的幾率大大減小,有可能僅僅只差一步,但是fast恰好越過slow,類似于死循環了。
這一點可以證明一下它們一定相遇

bool hasCycle(struct ListNode *head) {struct ListNode* slow=head;struct ListNode* fast=head;while(fast && fast->next){fast=fast->next->next;slow=slow->next;if(fast==slow)return true;}return false; }

十:環狀鏈表2


思路一

struct ListNode *detectCycle(struct ListNode *head) {if(head == NULL)//如果鏈表為空,直接返回空return NULL;struct ListNode* slow=head;struct ListNode* fast=head;struct ListNode* cur1=head;struct ListNode* cur2=NULL;//cur1和cur2分別指向斷開后的兩個鏈表的頭部int lengthA=0;int lengthB=0;int gap=0;while(fast && fast->next){slow=slow->next;fast=fast->next->next;if(fast==slow){break;//鏈表有環}}if(fast==NULL)//做安全處理,防止fast=NULL,而執行fast=fast->NULLreturn NULL;fast=fast->next;//斷開操作slow->next=NULL;cur2=fast;while(cur1)//下面全是相交鏈表的代碼{++lengthA;cur1=cur1->next;}while(cur2){++lengthB;cur2=cur2->next;}struct ListNode* longlist=head;struct ListNode* shortlist=fast;if(lengthB>lengthA){longlist=fast;shortlist=head;}gap=abs(lengthA-lengthB);while(gap--){longlist=longlist->next;}while(longlist){if(longlist==shortlist)return longlist;longlist=longlist->next;shortlist=shortlist->next;}return NULL;}


思路二
這種思路,需要進行證明,且不太好理解,但是代碼十分簡單

struct ListNode *detectCycle(struct ListNode *head) {struct ListNode* slow=head;struct ListNode* fast=head;while(fast && fast->next){slow=slow->next;fast=fast->next->next;if(fast==slow)break;} if(fast==NULL || fast->next==NULL)return NULL;struct ListNode* cur=head;while(cur!=fast){cur=cur->next;fast=fast->next;}return cur; }

十一:復雜鏈表復制


struct Node* copyRandomList(struct Node* head) {if(head==NULL){return NULL;}struct Node* cur=head;while(cur)//復制鏈表,復制一個就把它接到原來的后面,相當于新的鏈表和原來的鏈表錯一位{struct Node* NewNode=(struct Node*)malloc(sizeof(struct Node));NewNode->next=NULL;NewNode->random=NULL;NewNode->val=cur->val;struct Node* next=cur->next;cur->next=NewNode;NewNode->next=next;cur=next;}cur=head;while(cur)//新鏈表結點的random等于,對應舊鏈表的結點random的下一個{struct Node* behind=cur->next;if(cur->random!=NULL)behind->random=cur->random->next;elsebehind->random=NULL;cur=cur->next->next;}cur=head;struct Node* returnhead=head->next;while(cur)//把鏈表斷開{struct Node* NewNode=cur->next;struct Node* next=NewNode->next;cur->next=next;if(next==NULL){NewNode->next=NULL;break;}NewNode->next=next->next;cur=next;}return returnhead;}

十二:鏈表插入排序

將一個單鏈表進行插入排序

此題,可以使用一個sorthead保存頭結點,然后剩余結點挨個插入,具體插入時注意以下細節

struct ListNode* insertionSortList(struct ListNode* head) {if(head==NULL)//如果傳回來的是空鏈表,直接返回{return NULL;}struct ListNode*next=NULL;struct ListNode* cur=NULL;struct ListNode* sorthead=head;//使用sorthead保存headhead=head->next;從head的下一個結點開始逐個插入sorthead->next=NULL;//注意這一點不要忘了,不然會陷入死循環while(head)//head一直往后走,作用時檢視每個待插入結點{if(head->val<sorthead->val)//第一種情況就是如果待插入的結點小于頭結點,那么進行頭插{next=head->next;head->next=sorthead;sorthead=head;//注意把頭更新head=next;}else//第二種情況是大于等于,此時就要在鏈表內進行逐個比較插入{cur=sorthead;//cur用于掃描sorthead這個鏈表while(cur){if(cur->next==NULL)//一個特殊情況——一旦掃描到最后一個結點,那么說明此時待插入的結點相較于sorthead中的所有結點來說是最大的,所有插入末尾即可{next=head->next;cur->next=head;cur=cur->next;head=next;cur->next=NULL;break;//一旦插入就跳出循環,不然會死循環}if(head->val<(cur->next)->val)//這里必須使用(cur->next)->val,不能用cur->val,因為在插入時我們要插入到前面,而單鏈表只可以找到后面{next=head->next;head->next=cur->next;cur->next=head;head=next;break;//注意跳出}else{cur=cur->next;//如果不符合上述兩種情況,那么cur向后走,繼續比較}}}}return sorthead; }

十三:鏈表移除全部重復元素


此題思路不難,但是需要考慮的情況較多,容易寫錯
首先是正常情況1->2->3->3->4->4->5



鏈表由于其特殊性,所以出錯的地方往往就是頭或尾,而本題沒有通過全部用例,也正是因為頭尾這個特殊情況需要特殊處理
特殊情況一1->1->1->3->4


特殊情況2:1-2-3-3-3


最后:

class Solution { public:ListNode* deleteDuplication(ListNode* pHead){if(pHead==NULL || pHead->next==NULL){return pHead;}ListNode* prev=NULL;ListNode* cur=pHead;ListNode* next=cur->next;while(next){if( cur->val != next->val){prev=cur;cur=next;next=next->next;}else{while(next && cur->val==next->val){next=next->next;}if(prev!=NULL){prev->next=next;}else{pHead=next;}cur=next;if(next)next=cur->next;}}return pHead;} };

總結

以上是生活随笔為你收集整理的链表经典题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。