【LeetCode】鏈表精選12題

目錄

快慢指針:

1. 相交鏈表(簡單)

2. 環形鏈表(簡單)

3. 快樂數(簡單)

4. 環形鏈表 II(中等)

5. 刪除鏈表的倒數第 N 個節點(中等)

遞歸迭代雙解法:

1. 合并兩個有序鏈表(簡單)

1.1 遞歸求解

1.2 迭代求解

2. 反轉鏈表(簡單)

2.1 遞歸求解

2.2 迭代求解

3. 兩兩交換鏈表中的節點(中等)

3.1 遞歸求解

3.2 迭代求解

4. 合并 K 個升序鏈表(困難)

4.1 遞歸解法

4.2 迭代解法

綜合題:

1. 隨機鏈表的復制(中等)

2. 重排鏈表(中等)

3. K個一組翻轉鏈表(困難)


快慢指針:

1. 相交鏈表(簡單)

找兩個鏈表的尾結點,尾結點不相同則不相交。假設相交,長短鏈表之間差距gap步。假設i指向長鏈表的頭節點,j指向短鏈表的頭節點,i先走gap步,然后ij同時走,每次走1步。當ij相遇時,相遇點就是相交點。

class Solution {
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {// 找兩個鏈表的尾結點,尾結點不相同則不相交ListNode* tailA = headA;ListNode* tailB = headB;int lenA = 0;int lenB = 0;while (tailA->next){++lenA;tailA = tailA->next;}while (tailB->next){++lenB;tailB = tailB->next;}if (tailA != tailB)return nullptr;// 判斷長短鏈表ListNode* longList = headA;ListNode* shortList = headB;if (lenB > lenA){longList = headB;shortList = headA;}// 長鏈表先走gap步int gap = abs(lenA - lenB);while (gap--){longList = longList->next;}// 同時走,找交點while (longList != shortList){longList = longList->next;shortList = shortList->next;}return longList;}
};

2. 環形鏈表(簡單)

慢指針每次走1步,快指針每次走2步,慢指針進環后,快指針一定能追上慢指針,它們會在環中某點相遇。

為什么慢指針每次走1步,快指針要每次走2步,它們才能相遇?

假設慢指針進環時,快慢指針之間差距gap步。

如果快指針每次走2步,每走一次,它們之間的差距減1,gap一定會減到0。

如果快指針每次走3步,每走一次,它們之間的差距減2。如果gap為偶數,gap一定會減到0。如果gap為奇數,gap會減到-1,表示它們之間的距離變成C - 1(C是環的周長),如果C - 1是偶數,它們會相遇,如果C - 1是奇數,它們永遠不會相遇。

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

3. 快樂數(簡單)

不是鏈表題,但是和上一題“環形鏈表”類似,慢指針每次走1步,快指針每次走2步,慢指針進環后,快指針一定能追上慢指針,它們會在環中某點相遇。如果相遇點為1,則為快樂數,否則不是快樂數。這里的指針表示的是值本身。

class Solution {
public:bool isHappy(int n) {int slow = n;int fast = bitSquareSum(n);while (slow != fast){slow = bitSquareSum(slow);fast = bitSquareSum(bitSquareSum(fast));}return slow == 1;}private:// 計算n的每一位的平方和int bitSquareSum(int n){int sum = 0;while (n){int tmp = n % 10;sum += tmp * tmp;n /= 10;}return sum;}
};

4. 環形鏈表 II(中等)

慢指針每次走1步,快指針每次走2步,慢指針進環后,快指針一定能追上慢指針,它們會在環中某點相遇。

假設在相遇點,慢指針一共走了k步,那么快指針一定一共走了2k步,所以快指針比慢指針多走了k步。另外,在相遇點,快指針一定比慢指針在環中多走了若干圈。所以,k一定是環的周長(環中節點個數)的整數倍。

此時,讓i指向相遇點,j指向鏈表頭節點,它們之間差距k步(慢指針走過的步數),如果i到達了環的入口,j也一定到達了環的入口,因為它們之間差距k步,k一定是環的周長的整數倍。

class Solution {
public:ListNode *detectCycle(ListNode *head) {ListNode* fast = head;ListNode* slow = head;while (fast && fast->next){fast = fast->next->next;slow = slow->next;if (fast == slow) // 相遇{ListNode* i = slow; // 相遇點ListNode* j = head;while (i != j){i = i->next;j = j->next;}return i;}}return nullptr;}
};

5. 刪除鏈表的倒數第 N 個節點(中等)

快指針先走n步,然后快慢指針同時走,每次走1步。當快指針指向最后一個節點時,慢指針指向倒數第n + 1個節點。

例如,刪除鏈表的倒數第2個節點:

class Solution {
public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* preHead = new ListNode(0, head); // 哨兵節點ListNode* fast = preHead; // 快指針ListNode* slow = preHead; // 慢指針// 快指針先走n步while (n--){fast = fast->next;}// 快慢指針同時走,每次走1步,直到快指針走到最后一個節點停止while (fast->next){fast = fast->next;slow = slow->next;}// 此時慢指針指向倒數第n+1個節點// 讓倒數第n+1個節點的next域直接指向倒數第n-1個節點slow->next = slow->next->next;return preHead->next;}
};

遞歸迭代雙解法:

1. 合并兩個有序鏈表(簡單)

1.1 遞歸求解

重復的子問題——函數頭設計

ListNode*?mergeTwoLists(ListNode*?list1,?ListNode*?list2)

子問題在做什么——函數體設計

選擇兩個鏈表的頭節點中值較小的那一個作為最終合并的新鏈表的頭節點,然后將剩下的鏈表交給遞歸函數去處理。

  1. 比較list1->val和list2->val的大小(假設list1->val較小)
  2. list1->next?=?mergeTwoLists(list1->next,?list2);
  3. return list1;

遞歸出口

當某一個鏈表為空的時候,返回另外一個鏈表。

class Solution {
public:ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {if (list1 == nullptr)return list2;if (list2 == nullptr)return list1;if (list1->val < list2->val){list1->next = mergeTwoLists(list1->next, list2);return list1;}else{list2->next = mergeTwoLists(list1, list2->next);return list2;}}
};

1.2 迭代求解

class Solution {
public:ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {ListNode* preHead = new ListNode; // 哨兵節點ListNode* tail = preHead;// 取小的尾插while (list1 && list2){if (list1->val < list2->val){tail->next = list1;tail = tail->next;list1 = list1->next;}else{tail->next = list2;tail = tail->next;list2 = list2->next;}}if (list1){tail->next = list1;}if (list2){tail->next = list2;}return preHead->next;}
};

2. 反轉鏈表(簡單)

2.1 遞歸求解

重復的子問題——函數頭設計

ListNode*?reverseList(ListNode*?head)

子問題在做什么——函數體設計

將當前結點之后的鏈表反轉,然后把當前結點添加到反轉后的鏈表后面即可,返回反轉后的頭節點。

  1. ListNode* newHead = reverseList(head->next);
  2. head->next->next = head;? ? head->next?=?nullptr;
  3. return?newHead;

遞歸出口

當前結點為空或者當前只有一個結點的時候,不用反轉,直接返回。

class Solution {
public:ListNode* reverseList(ListNode* head) {if (head == nullptr || head->next == nullptr)return head;ListNode* newHead = reverseList(head->next);head->next->next = head;head->next = nullptr;return newHead;}
};

2.2 迭代求解

class Solution {
public:ListNode* reverseList(ListNode* head) {ListNode* pre = nullptr;ListNode* cur = head;while (cur){ListNode* next = cur->next;cur->next = pre;pre = cur;cur = next;}return pre;}
};

3. 兩兩交換鏈表中的節點(中等)

3.1 遞歸求解

重復的子問題——函數頭設計

ListNode*?swapPairs(ListNode*?head)

子問題在做什么——函數體設計

將從第三個節點開始的鏈表兩兩交換節點,然后再把前兩個節點交換一下,鏈接上剛才處理過的鏈表,并返回。

  1. ListNode*?tmp?=?swapPairs(head->next->next);
  2. ListNode*?newHead?=?head->next;? ? newHead->next?=?head;
  3. head->next?=?tmp;
  4. return?newHead;

遞歸出口

當前結點為空或者當前只有一個結點的時候,不用兩兩交換,直接返回。

class Solution {
public:ListNode* swapPairs(ListNode* head) {if (head == nullptr || head->next == nullptr)return head;ListNode* tmp = swapPairs(head->next->next);ListNode* newHead = head->next;newHead->next = head;head->next = tmp;return newHead;}
};

3.2 迭代求解

class Solution {
public:ListNode* swapPairs(ListNode* head) {ListNode* preHead = new ListNode(0, head); // 哨兵節點ListNode* cur = preHead;// cur后面的兩個節點交換while (cur->next && cur->next->next){ListNode* node1 = cur->next;ListNode* node2 = cur->next->next;cur->next = node2;node1->next = node2->next;node2->next = node1;cur = node1;}return preHead->next;}
};

4. 合并 K 個升序鏈表(困難)

4.1 遞歸解法

分治的思想,類似歸并排序:

  1. 劃分兩個子區間

  2. 分別對兩個子區間的鏈表進行合并,形成兩個有序鏈表

  3. 合并兩個有序鏈表

重復的子問題——函數頭設計

ListNode*?merge(vector<ListNode*>& lists, int begin, int end)

子問題在做什么——函數體設計

  1. 劃分兩個子區間:int mid = (begin + end) / 2;
  2. 遞歸合并兩個子區間:
    ListNode* l1 = merge(lists, begin, mid);
    ListNode* l2 = merge(lists, mid + 1, end);
  3. 合并兩個有序鏈表:return?mergeTowList(l1, l2);

遞歸出口

當區間只有一個鏈表時,不合并。另外,當題目給出空鏈表時,不合并。

class Solution {
public:ListNode* mergeKLists(vector<ListNode*>& lists) {return merge(lists, 0, lists.size() - 1);}private:ListNode* merge(vector<ListNode*>& lists, int begin, int end){if (begin > end)return nullptr;if (begin == end)return lists[begin];int mid = (begin + end) / 2;ListNode* l1 = merge(lists, begin, mid);ListNode* l2 = merge(lists, mid + 1, end);return mergeTwoLists(l1, l2);}ListNode* mergeTwoLists(ListNode* list1, ListNode* list2){ListNode* preHead = new ListNode; // 哨兵節點ListNode* tail = preHead;// 取小的尾插while (list1 && list2){if (list1->val < list2->val){tail->next = list1;tail = tail->next;list1 = list1->next;}else{tail->next = list2;tail = tail->next;list2 = list2->next;}}if (list1){tail->next = list1;}if (list2){tail->next = list2;}return preHead->next;}
};

4.2 迭代解法

和“合并兩個有序鏈表”類似,就是取小的尾插。怎么判斷K個鏈表未合并的頭節點中最小的那個?利用堆這個數據結構即可。把K個鏈表未合并的頭節點放進一個小根堆,堆頂就是最小的那個。

class Solution {
public:ListNode* mergeKLists(vector<ListNode*>& lists) {// 創建小根堆priority_queue<ListNode*, vector<ListNode*>, cmp> heap;// 將所有頭節點放進小根堆for (auto& l : lists){if (l){heap.push(l);}}// 合并鏈表ListNode* preHead = new ListNode; // 哨兵節點ListNode* tail = preHead;while (!heap.empty()){// 取堆頂節點尾插tail->next = heap.top();heap.pop();tail = tail->next;// 將剛才合并的節點的下一個節點補充進堆if (tail->next){heap.push(tail->next);}}return preHead->next;}private:struct cmp{bool operator()(ListNode* n1, ListNode* n2){return n1->val > n2->val;}};
};

綜合題:

1. 隨機鏈表的復制(中等)

class Solution {
public:Node* copyRandomList(Node* head) {if (head == nullptr)return nullptr;// A->B->C->null --> A->A'->B->B'->C->C'->nullNode* cur = head;while (cur){Node* copy = new Node(cur->val); // 拷貝結點copy->next = cur->next;cur->next = copy;cur = cur->next->next;}// 設置拷貝結點的random,假如A的random域指向C,就讓A'的random域指向C'cur = head;while (cur){Node* copy = cur->next;if (cur->random == nullptr){copy->random = nullptr;}else{copy->random = cur->random->next;}cur = cur->next->next;}// 將A'、B'、C'鏈接在一起,并且還原原鏈表Node* preHead = new Node(0); // 哨兵節點Node* tail = preHead;cur = head;while (cur){tail->next = cur->next;tail = tail->next;cur->next = cur->next->next;cur = cur->next;}return preHead->next;}
};

2. 重排鏈表(中等)

把鏈表后半段反轉,再合并起來:

鏈表長度是偶數:1 2 3 4? ??(2是中間節點)

1 2

4 3

合并起來:1 4 2 3

鏈表長度是奇數:1 2 3 4 5? ??(3是中間節點)

1 2 3

5 4(4 5反轉)

合并起來:1 5 2 4 3

class Solution {
public:void reorderList(ListNode* head) {ListNode* mid = midNode(head);ListNode* l2 = reverseList(mid->next);mid->next = nullptr;ListNode* l1 = head;mergeLists(l1, l2);}private:// 快慢指針找鏈表的中間節點,如果節點個數為偶數,取靠左的ListNode* midNode(ListNode* head){ListNode* fast = head;ListNode* slow = head;// 慢指針每次走1步,快指針每次走2步// 如果節點個數為奇數,當快指針指向最后一個節點時,慢指針指向中間節點// 如果節點個數為奇數,當快指針指向倒數第二個節點時,慢指針指向靠左的中間節點while (fast->next && fast->next->next){fast = fast->next->next;slow = slow->next;}return slow;}// 反轉鏈表ListNode* reverseList(ListNode* head) {ListNode* pre = nullptr;ListNode* cur = head;while (cur){ListNode* next = cur->next;cur->next = pre;pre = cur;cur = next;}return pre;}// 合并鏈表void mergeLists(ListNode* l1, ListNode* l2){ListNode* cur1 = l1;ListNode* cur2 = l2;while (cur1 && cur2){ListNode* next1 = cur1->next;ListNode* next2 = cur2->next;cur1->next = cur2;cur2->next = next1;cur1 = next1;cur2 = next2;}}
};

3. K個一組翻轉鏈表(困難)

頭插法。

class Solution {
public:ListNode* reverseKGroup(ListNode* head, int k) {// 求出需要翻轉多少組int n = 0;ListNode* cur = head;while (cur){cur = cur->next;n++;}n /= k;// 重復n次:長度為k的鏈表翻轉ListNode* preHead = new ListNode; // 哨兵節點ListNode* pre = preHead;cur = head;for (int i = 0; i < n; i++){ListNode* tmp = cur;for (int j = 0; j < k; j++){ListNode* next = cur->next;cur->next = pre->next;pre->next = cur;cur = next;}pre = tmp;}// 把不需要翻轉的部分接上pre->next = cur;return preHead->next;}
};

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/529118.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/529118.shtml
英文地址,請注明出處:http://en.pswp.cn/news/529118.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python類的屬性和對象屬性_python 類屬性、對象屬性-阿里云開發者社區

類的普通屬性:  dir(Myclass), 返回一個key列表&#xff1b; Myclass.__dir__,返回一個字典&#xff1b; 1、類的數據屬性&#xff1b; 2、類的方法&#xff1b; 類的特殊屬性&#xff1a; 1、Myclass.__name__  類的名字 2、Myclass.__doc__   類的文檔字符串 3、Mycla…

擊鼓傳花c語言編程題,c語言-第5章 循環程序設計.ppt

《c語言-第5章 循環程序設計.ppt》由會員分享&#xff0c;可在線閱讀&#xff0c;更多相關《c語言-第5章 循環程序設計.ppt(83頁珍藏版)》請在人人文庫網上搜索。1、第5章 循環程序設計,管理學院 電子商務系,2,第5章 循環程序設計,5.1 概述 5.2 while和do while循環 5.3 for循環…

python快速檢測視頻跳過幀_python實現視頻分幀效果

本文實例為大家分享了python實現視頻分幀的具體代碼&#xff0c;供大家參考&#xff0c;具體內容如下 import cv2 vidcap cv2.VideoCapture(005.avi) success,image vidcap.read() count 0 success True while success: success,image vidcap.read() cv2.imwrite("fr…

最大素數c語言,for語句計算輸出10000以內最大素數怎么搞最簡單??各位大神們...

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓#include #include int* pt NULL; // primes_tableint pt_size 0; // primes_table 數量大小int init_primes_table(void){FILE* pFile;pFile fopen("primes_table.bin", "rb");if (pFile NULL) {fputs(&q…

python去重保留唯一一個值_Python DataFrame使用drop_duplicates()函數去重(保留重復值,取重復值)...

摘要 在進行數據分析時&#xff0c;我們經常需要對DataFrame去重&#xff0c;但有時候也會需要只保留重復值。 這里就簡單的介紹一下對于DataFrame去重和取重復值的操作。 創建DataFrame 這里首先創建一個包含一行重復值的DataFrame。2.DataFrame去重&#xff0c;可以選擇是否保…

自定義日歷控android,Android 一個日歷控件的實現小記

先看幾張動態的效果圖吧&#xff01;這里主要記錄一下在編寫日歷控件過程中一些主要的點&#xff1a;一、主要功能1、支持農歷、節氣、常用節假日2、日期范圍設置&#xff0c;默認支持的最大日期范圍[1900.1~2049.12]3、禁用日期范圍設置4、初始化選中單個或多個日期5、單選、多…

python先返回再處理_python xpath解析返回對象怎么處理

3 4 5 text 6 7 ... 8 ... 9 ......10 11 12 ...13 ...14 ......15 16 17 18

android文件讀取工具類,Android 下讀取Assets Properties操作封裝工具類

Android 下讀取Assets Properties操作封裝工具類發布時間&#xff1a;2018-06-03作者&#xff1a;laosun閱讀(2081)為了方便使用&#xff0c;首先創建BaseApplication類&#xff0c;如下所示&#xff1a;import android.app.Application;import android.content.Context;/*** C…

python粘性拓展_如何將tkinter小部件置于粘性框架中

在google中使用“如何使tkinter網格擴展”&#xff0c;我遇到了這個問題。 引用布萊恩奧克利的話Rows and columns have "weight" which describes how they grow or shrink to fill extra space >in the master. By default a row or column has a weight of zer…

android 固件 編輯器,RK3288編譯 Android 5.1 固件

1 準備工作編譯 Android 對機器的配置要求較高&#xff1a;64 位 CPU16GB 物理內存交換內存30GB 空閑的磁盤空間用于構建&#xff0c;源碼樹另外占用大約 25GBUbuntu 14.04 操作系統八核i7&#xff0c;編譯完成需要一個半小時安裝 JDK 7:sudo apt-get install openjdk-7-jdkUbu…

python解壓到指定文件夾_在Python中壓縮和解壓文件

Python部落(python.freelycode.com)組織翻譯&#xff0c;禁止轉載&#xff0c;歡迎轉發。 如果你已經使用計算機一段時間&#xff0c;你可能遇到了.zip擴展名的文件。它們是可以保存許多其他文件&#xff0c;文件夾和子文件夾的壓縮內容的特殊文件。這種類型的文件在使用互聯網…

android bar布局,Android學習路線(十)如何將Action Bar疊放在你的布局上

默認狀況下&#xff0c;action bar出如今activity窗口的頂部&#xff0c;略微減小了activity布局的總空間。若是你想隱藏或者顯示action bar&#xff0c;在這堂用戶體驗的課程中&#xff0c;你能夠經過調用htmlFigure 1. Gallerys action bar in overlay mode.android為了不在a…

geant4運行例子_Geant4--一次編譯,運行多個Run,極大提升模擬效率

文|梁佐佐應唐光毅博士/后之約&#xff0c;對于Geant4模擬&#xff0c;我們看是否能解決這么一個問題&#xff1a;我現在想模擬探測器不同角度下的響應&#xff0c;每次模擬需要/run/beamOn 100&#xff0c; 可是我真的不想一遍一遍的去http://DetectorConstruction.cc中修改幾…

python3.7基礎教程_關于本教程 |《Python 官方文檔:入門教程 3.7.0》| Python 技術論壇...

本文檔最新版為 3.8&#xff0c;舊版本可能放棄維護&#xff0c;推薦閱讀最新版&#xff01; Python 入門教程 Python 是一門簡單易學且功能強大的編程語言。它擁有高效的高級數據結構&#xff0c;并能夠用簡單又有效的方式進行面向對象編程。Python 優雅的語法和動態類型&…

android listview countdowntimer,Android-ListView中的CountDownTimer隨機閃爍

我正在使用計時器制作列表視圖&#xff0c;每個計時器都有不同的截止日期&#xff0c;具體取決于數據庫(類似于拍賣)Time now new Time();now.setToNow();now.normalize(true);nowMillis now.toMillis(true);..String endtime a.get(position).get(TAG_ENDTIME);Integer tim…

echart實現3d地圖_3D飛線效果——讓線“飛”起來的秘密

在城市規劃、統計、交通等行業&#xff0c;地圖可視化已成為最直接也最吸引眼球的一種表達方式。例如人群遷徙、人口流動、OD出行、職住分析、客流來源等眾多場景都需要用到飛線效果呈現。2D飛線效果圖隨著可視化技術的進一步發展&#xff0c;傳統的2D飛線效果略顯單調&#xf…

ad域管理與維護_在NAS SMB卷上使用VisualSVN Server維護代碼庫

VisualSVN Server[1] 是 Windows 平臺上流行的 SVN 形式的代碼管理工具。以下我們將介紹把 NAS SMB 卷作為 VisualSVN 代碼庫存儲中心時會遇到的幾個問題以及相應的解決方法。1. 安裝錯誤的解決方法我們以 VisualSVN Server 3.3.1 版本為例&#xff0c;在安裝 VisualSVN Server…

android 開發art,Android應用開發之Android 系統啟動原理(art 虛擬機)

本文將帶你了解Android應用開發之Android 系統啟動原理(art 虛擬機)&#xff0c;希望本文對大家學Android有所幫助。Android 系統啟動原理(art 虛擬機)一、虛擬機的啟動Android 是一個 Linux 的虛擬機&#xff0c;當虛擬機啟動的時候&#xff0c;會執行手機根目錄下的 init.r…

電腦文件夾可以分屏的軟件_電腦上什么便簽軟件可以添加音頻?

提及便簽&#xff0c;很多人都會很自然地想到手機便簽。這是因為隨著智能手機和移動互聯網的發展&#xff0c;現在很多手機上都有了系統自帶的便簽app。其實&#xff0c;除了手機便簽外&#xff0c;還有電腦便簽呢&#xff01;這不&#xff0c;Win7及其以上版本的電腦上還有系統…

jsp form提交到后臺中文亂碼_JSP與servlet之間的數據傳遞

【51】Jsp與Servlet之間的傳值有兩種&#xff0c;一種是Jsp傳值給Sevlet&#xff0c;另一種是Servlet傳值給Jsp&#xff1b;使用request、response對象完成傳值&#xff0c;具體實現如下&#xff1a;Jsp與Servlet之間的傳值有兩種&#xff0c;一種是Jsp傳值給Sevlet&#xff0c…