1.鏈表中環的入口節點數據結構
首先判斷頭指針是否是空的
而後須要判斷這個鏈表中包不包含環:兩個指針,一個一步一個兩部,若是相遇,說明存在
而後判斷環節點的個數:從相遇的位置開始,往前走並計數,直到和本身再次相遇,獲得個數
而後找出入口節點:從頭開始,倆指針一個先走n步,另外一個再走,兩個相遇的位置就是入口節點位置dom
2.翻轉鏈表oop
須要判斷鏈表是否是空的或者鏈表是否是隻有一個節點,若是是的話,直接就輸出原來的鏈表了;spa
若是不是的話,翻轉利用循環來作,由於須要將當前節點指向前一個節點,因此後一個節點須要先保存位置,也就是須要三個指針,一個pPre,一個pCur,一個pNext:先保存後一個節點,而後把當前節點指向前一個節點,而後把當前節點變成上一個節點,下一個節點變成當前節點;注意翻轉以後頭結點是原來的最後一個節點。指針
3.從尾到頭打印鏈表code
思路(1):能夠先翻轉鏈表,再逐個輸出blog
思路(2):這種具備「後進先出」特性的,用棧比較容易,建立一個棧存放鏈表的節點,存放完以後從棧頂依次取出便可ip
4.兩個鏈表的第一個公共節點it
舉例子:
1 2 5 9 6 3 0
7 8 9 6 3 0 io
首先須要知道的是,兩個鏈表從公共節點開始,以後的節點確定都是如出一轍的;
能夠先遍歷獲得兩個鏈表各自的長度,而後讓長的那個先走比另外一個長出的步數,再同時走,判斷哪裏相等哪裏就是第一個公共節點了
5.鏈表中倒數第k個節點
單向鏈表確定是不能從後往前數的,這個跟上面有的相似,既然知道是倒數第k個,那就兩個指針,讓一個先走k-1步,而後兩個同時走,判斷先走的那個到尾結點了,那後走的那個就是倒數第k個節點。
6.刪除鏈表中重複的節點
例如:1 2 3 3 4 4 5結果是1 2 5
首先須要判斷頭指針(第一個節點)和第二個節點是否是空的,若是是,返回頭指針就好了;
正常狀況的時候,由於有可能存在刪除第一個節點的狀況,因此須要先從新建立一個頭指針ListNode* newHead = new ListNode(-1),而後把這個頭指針賦初值指向本來的頭指針newHead->next = pHead;
而後須要三個指針來解決這個問題,分別是pPre pCur pNext三個,pPre 賦初值newHead,pCur賦初值pHead, 利用當前節點的循環來進行:得判斷當前節點和當前節點的下一個節點不爲空才進入循環來查找和刪除,由於裏頭要對節點進行刪除,因此要先保存下一個節點,而後若是當前節點等於下一個節點的值,由於還要繼續判斷下一位,來一個循環,只要下一個節點和當前節點的值相等,就把pNext日後移一個,直到找到不相等的就退出這個查找的循環了;而後執行刪除,也就是把上一個節點pPre的下一個節點變成pNext,當前操做循環的節點變成pNext,而後再去循環判斷;
那若是當前節點和下一個節點的值不相等呢:指針日後挪,循環再判斷,也就是pPre = pCur;pCur = pCur->next。
最後返回新的頭結點newHead的下一個節點便可。
7.複製複雜的鏈表
分紅三個步驟:
(1)複製原始鏈表的任意節點N並建立新節點N',把節點N'連接到節點N的後面
(2)設置複製出來的節點的random,假設原始鏈表上的N的random指向節點S,那麼其對應複製出來的N'是N的next指向的節點,一樣S'也是S的next指向的節點
(3)把整個鏈表根據奇數偶數分出兩個鏈表來,偶數的就是拷貝的鏈表
/* struct RandomListNode { int label; struct RandomListNode *next, *random; RandomListNode(int x) : label(x), next(NULL), random(NULL) { } }; */ class Solution { public: RandomListNode* Clone(RandomListNode* pHead) { if(pHead == nullptr) return nullptr; //分紅三個步驟: //1.複製原始鏈表的任意節點N並建立新節點N',把節點N'連接到節點N的後面 //2.設置複製出來的節點的random,假設原始鏈表上的N的random指向節點S,那麼其對應複製出來的N'是N的next指向 //的節點,一樣S'也是S的next指向的節點 //3.把整個鏈表根據奇數偶數分出兩個鏈表來,偶數的就是拷貝的鏈表 ClonedNode(pHead); SetRandom(pHead); return ReconnectNodes(pHead); } void ClonedNode(RandomListNode* pHead) { RandomListNode* pNode = pHead; while(pNode != nullptr) { RandomListNode* newNode = new RandomListNode(0);//建立新節點 newNode->label = pNode->label; newNode->next = pNode->next; newNode->random = nullptr; pNode->next = newNode; pNode = newNode->next; } } void SetRandom(RandomListNode* pHead) { RandomListNode* pNode = pHead; while(pNode != nullptr) { RandomListNode* pCloned = pNode->next; if(pNode->random != nullptr)//這一步判斷別忘了,若是爲空,會形成程序癱瘓 pCloned->random = pNode->random->next; else pCloned->random = nullptr; pNode = pCloned->next;//pNode日後移一位 } } RandomListNode* ReconnectNodes(RandomListNode* Head) { RandomListNode* pNode = Head;//循環操做 RandomListNode* pClonedHead = nullptr; RandomListNode* pClonedNode = nullptr;//建立一個新節點,做爲拷貝鏈表的頭結點 if(pNode != nullptr) { pClonedHead = pClonedNode = pNode->next; pNode->next = pClonedNode->next; pNode = pNode->next; } while(pNode != nullptr) { pClonedNode->next = pNode->next; pClonedNode = pClonedNode->next; pNode->next = pClonedNode->next; pNode = pNode->next; } return pClonedHead; } };
8.翻轉鏈表
給定一個鏈表,旋轉鏈表,將鏈表每一個節點向右移動 k 個位置,其中 k 是非負數。
這個題說是翻轉鏈表,其實就是改變了鏈表的頭尾節點而已。
(1)先判斷特殊狀況,好比鏈表是否爲空,是否只有一個元素,翻轉的k是否爲0等,不然直接返回
(2)而後處理通常狀況,得先遍歷一次獲得鏈表的總長度size,而後將鏈表首尾相接,成爲一個循環鏈表
(3)根據size和k的關係,計算出循環鏈表循環的次數loot= size - (k%size);
(4)而後進行指針循環,以前的頭尾節點指針開始移動,次數爲loop,直到移動結束,原先的尾結點指針指向的爲新鏈表的尾部,原先的頭結點指針指向的爲新鏈表的頭部。
9.扁平化多級雙向鏈表
多級雙向鏈表中,除了指向下一個節點和前一個節點指針以外,它還有一個子鏈表指針,可能指向單獨的雙向鏈表。這些子列表也可能會有一個或多個本身的子項,依此類推,生成多級數據結構,以下面的示例所示。
給你位於列表第一級的頭節點,請你扁平化列表,使全部結點出如今單級雙鏈表中。
這題太經典了!
/* // Definition for a Node. class Node { public: int val; Node* prev; Node* next; Node* child; }; */ class Solution { public: vector<Node*> v; //這題就是二叉樹的前序遍歷 //能夠先前序遍歷,將全部節點放入容器中,而後再順序進行先後鏈接 void dfs(Node* head) { if(head == nullptr) return; v.push_back(head); dfs(head->child); dfs(head->next); } Node* flatten(Node* head) { dfs(head);//這樣就將全部的節點放入了容器中,按照前序的方式 int n = v.size();//獲得容器中的節點數 for(int i=0;i<n;i++) { if(i<n-1) v[i]->next = v[i+1]; if(i>0) v[i]->prev = v[i-1]; v[i]->child = nullptr; } return head; } };
10.判斷是否是迴文鏈表
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: bool isPalindrome(ListNode* head) { //先判斷特殊狀況 if(head == nullptr || head->next == nullptr) return true; //而後通常狀況的判斷方式是:先找到鏈表的中點,而後翻轉後半部分,而後進行比較 ListNode* fast = head; ListNode* slow = head; while(fast != nullptr) { slow = slow->next; fast = fast->next? fast->next->next : fast->next; } //最後的slow爲原來鏈表的中間節點,也是待會兒須要翻轉鏈表的頭結點 //fast則指向了nullptr //而後須要進行翻轉 ListNode* pPre = nullptr; while(slow != nullptr) { ListNode* temp = slow->next;//存儲下一個節點 slow->next = pPre; pPre = slow; slow = temp; } //而後進行判斷 while(pPre != nullptr && head != nullptr) { if(pPre->val != head->val) return false; pPre = pPre->next; head = head->next; } return true; } };
tips:遇到鏈表爲題時候的思考方式
(1)是否能用雙指針解決
(2)知否能夠將一個指針先走幾步,再兩個同時走解決
(3)是否須要知道鏈表節點的個數
(4)是否須要將鏈表變成循環鏈表處理
(5)考慮是否須要容器,容器能夠放節點,容器的好處也是能知道鏈表的長度
(6)找鏈表的中點、翻轉鏈表都是最基本的操做,在拔高的題目裏頭有可能會用到