(一)LeetCode206.反轉(zhuǎn)鏈表
題目描述:
反轉(zhuǎn)一個(gè)單鏈表。
代碼實(shí)現(xiàn)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
ListNode* reverseList(ListNode* head)//非遞歸版本
{
if (head == NULL)
return head;
ListNode * pre = NULL;
ListNode * p = head;
while (p)
{
ListNode *tmp = p->next;
p->next = pre;
pre = p;
p = tmp;
}
return pre;
}
ListNode* reverseList1(ListNode* head)//遞歸版本
{
if (head == NULL || head->next == NULL)
return head;
ListNode *newHead = reverseList(head->next);
//反轉(zhuǎn)指針
ListNode * tmp = head->next;
tmp->next = head;
head->next = NULL;
return newHead;
}
(二)LeetCode160. 相交鏈表
題目描述
編寫一個(gè)程序,找到兩個(gè)單鏈表相交的起始節(jié)點(diǎn)。
思路
假設(shè)鏈表A與B一共有三部分構(gòu)成,A獨(dú)有的結(jié)點(diǎn)個(gè)數(shù)為a,B獨(dú)有的結(jié)點(diǎn)個(gè)數(shù)為b,A,B公有的結(jié)點(diǎn)個(gè)數(shù)為c.
則有 a+c+b=b+c+a
那么可以用兩個(gè)指針p,q,分別從A,B的頭結(jié)點(diǎn)開始,當(dāng)p指向空的時(shí)候,將p指向B的頭結(jié)點(diǎn),同理當(dāng)q指向空的時(shí)候,將q指向A的頭結(jié)點(diǎn)。
如果兩個(gè)鏈表有相交部分,他們一定會(huì)相遇,如果沒有,最后都同時(shí)為空。
代碼
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB)
{
ListNode * p = headA;
ListNode * q = headB;
if (!p || !q)
return NULL;
while (p != q)
{
if (!p)
p = headB;
else
p = p->next;
if (!q)
q = headA;
else
q = q->next;
}
return p;
}
(三)LeetCode142. 環(huán)形鏈表 II
題目描述
給定一個(gè)鏈表,返回鏈表開始入環(huán)的第一個(gè)節(jié)點(diǎn)。 如果鏈表無環(huán),則返回 null。
思路
快慢指針即可解決,快指針走兩步,慢指針走一步。假如有環(huán),那么兩個(gè)指針一定會(huì)相遇
證明
假如存在環(huán),可以將問題看出快指針在追趕慢指針。
- 快指針與慢指針之間差1步。此時(shí)繼續(xù)往后走,慢指針前進(jìn)1步,快指針前進(jìn)2步,兩者相遇。
- 快指針與慢指針之間差2步。此時(shí)繼續(xù)往后走,慢指針前進(jìn)1步,快指針前進(jìn)2步,兩者之間相差1步,轉(zhuǎn)化為第1種情況。
- 快指針與慢指針之間差n步。此時(shí)繼續(xù)往后走,慢指針前進(jìn)1步,快指針前進(jìn)2步,兩者之間相差
(n+1-2)
步。
尋找環(huán)入口
假如快慢指針在環(huán)的i個(gè)結(jié)點(diǎn)相遇,假設(shè)慢指針一共走了n步,那么快指針走了2n步,因?yàn)榭熘羔樀乃俣仁瞧鋬杀叮敲绰羔樤僮遪步(剛好是2n步),肯定還是在i點(diǎn),所以可以得到環(huán)的大小為n
。
我們假設(shè)環(huán)入口與結(jié)點(diǎn)i的距離為k,起點(diǎn)距離環(huán)入口距離為l,
那么根據(jù)上述有n=l+k(慢指針第一次走n步)
,則l=n-k
所以,我們用兩個(gè)指針,分別從起點(diǎn)和相遇點(diǎn)同時(shí)走,則一定在環(huán)入口相遇
因?yàn)橄嘤鳇c(diǎn)距離環(huán)入口:n-k
起點(diǎn)距離環(huán)入口:l
代碼
ListNode *detectCycle (ListNode *head)
{
if (head == NULL || head->next == NULL)
return NULL;
ListNode * fast = head->next->next;
ListNode * slow = head->next;
while (fast != slow)
{
if (fast == NULL || slow == NULL || fast->next == NULL)
return NULL;
fast = fast->next->next;
slow = slow->next;
}
fast = head;
while (fast != slow)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
(四)LeetCode234. 回文鏈表
題目描述
判斷一個(gè)鏈表是否為回文鏈表。
思路
將鏈表后半段原地翻轉(zhuǎn),再將前半段、后半段依次比較,判斷是否相等
代碼
//鏈表后半段原地翻轉(zhuǎn),再將前半段、后半段依次比較,判斷是否相等
bool isPalindrome(ListNode* head)
{
//如果鏈表為空或者僅有一個(gè)元素那么肯定是回文鏈表
if (!head || !head->next)
return true;
//快慢指針法,尋找鏈表中心
ListNode * slow, *fast;
slow = fast = head;
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
if (fast) //鏈表元素奇數(shù)個(gè)
{
slow->next = reverseList(slow->next);
slow = slow->next;
}
else//鏈表元素偶數(shù)個(gè)
slow = reverseList(slow);
//slow此時(shí)為鏈表后半段第一個(gè)元素
while (slow)
{
if (head->val != slow->val)
{
return false;
}
slow = slow->next;
head = head->next;
}
return true;
}
//非遞歸版本的反轉(zhuǎn)鏈表
ListNode* reverseList(ListNode* head)
{
if (head == NULL)
return head;
ListNode * pre = NULL;
ListNode * p = head;
while (p)
{
ListNode *tmp = p->next;
p->next = pre;
pre = p;
p = tmp;
}
return pre;
}
(五)LeetCode109. 有序鏈表轉(zhuǎn)換二叉搜索樹
題目描述
給定一個(gè)單鏈表,其中的元素按升序排序,將其轉(zhuǎn)換為高度平衡的二叉搜索樹。
一個(gè)高度平衡二叉樹是指一個(gè)二叉樹每個(gè)節(jié)點(diǎn) 的左右兩個(gè)子樹的高度差的絕對值不超過 1。
思路
鏈表有序,二叉搜索樹的左子樹小于根結(jié)點(diǎn),右子樹大于根結(jié)點(diǎn),又需要滿足AVL的性質(zhì)。
則找到鏈表中間元素,設(shè)為根結(jié)點(diǎn),前半段為左子樹,后半段為右子樹,遞歸即可。
代碼
TreeNode* sortedListToBST(ListNode* head)
{
if (!head)
return NULL;
if (!head->next)
{
TreeNode * root = new TreeNode(head->val);
return root;
}
//快慢指針法,尋找鏈表中心
ListNode * slow, *fast;
slow = fast = head;
ListNode * tail = head;//保存前半段的尾巴
while (fast && fast->next)
{
tail = slow;
slow = slow->next;
fast = fast->next->next;
}
tail->next = NULL;//前半段
TreeNode * root = new TreeNode(slow->val);
root->left = sortedListToBST(head);
root->right = sortedListToBST(slow->next);
return root;
}
(六)LeetCode24. 兩兩交換鏈表中的節(jié)點(diǎn)
題目描述
給定一個(gè)鏈表,兩兩交換其中相鄰的節(jié)點(diǎn),并返回交換后的鏈表。
思路
三個(gè)指針,p表示上一組交換完成后的后面結(jié)點(diǎn),l,r表示當(dāng)前交換的左右結(jié)點(diǎn)
交換過程可以簡化為:p指向r,r指向l
即可,最后將p指向NULL
注意處理奇數(shù)長度的情況
代碼
ListNode* swapPairs(ListNode* head)
{
if (!head || !head->next)
return head;
ListNode *p, *l, *r; //p表示上一組交換完成后的后面結(jié)點(diǎn),l,r表示當(dāng)前交換的左右結(jié)點(diǎn)
p = NULL, l = head, r = head->next;
head = head->next; //頭結(jié)點(diǎn)更換位置
ListNode *l1, *r1; //記錄下兩組需要交換的結(jié)點(diǎn)
while (l && r)
{
l1 = r->next;
r1 = l1 ? l1->next : NULL;
if (p)//上一組交換完成后的后結(jié)點(diǎn)的 next指向當(dāng)前交換后的前結(jié)點(diǎn)
p->next = r;
r->next = l;//r指向l
p = l;//保存當(dāng)前交換后的后結(jié)點(diǎn)
l = l1;
r = r1;
}
if (l)//奇數(shù)長度情況
{
p->next = l;
p = l;
}
p->next = NULL;//鏈表尾端
return head;
}
(七)LeetCode328. 奇偶鏈表
題目描述
給定一個(gè)單鏈表,把所有的奇數(shù)節(jié)點(diǎn)和偶數(shù)節(jié)點(diǎn)分別排在一起。請注意,這里的奇數(shù)節(jié)點(diǎn)和偶數(shù)節(jié)點(diǎn)指的是節(jié)點(diǎn)編號的奇偶性,而不是節(jié)點(diǎn)的值的奇偶性。
思路
用兩個(gè)指針odd,even分別表示奇數(shù)指針和偶數(shù)指針
先更新odd的next,然后更新odd
再更新even的next,然后更新even
代碼
ListNode* oddEvenList(ListNode* head)
{
if (!head || !head->next)
return head;
ListNode *odd = head;//奇數(shù)結(jié)點(diǎn)
ListNode * even = head->next;//偶數(shù)結(jié)點(diǎn)
ListNode * evenHead = head->next;//偶數(shù)頭結(jié)點(diǎn)
while (odd->next && even->next)
{
//奇數(shù)結(jié)點(diǎn)下一個(gè)指向偶數(shù)結(jié)點(diǎn)的下一個(gè)元素
odd->next = even->next;//更新奇數(shù)結(jié)點(diǎn)的next
odd = odd->next;//更新奇數(shù)結(jié)點(diǎn)
//偶數(shù)結(jié)點(diǎn)下一個(gè)指向新的奇數(shù)結(jié)點(diǎn)的下一個(gè)元素
even->next = odd->next;//更新偶數(shù)結(jié)點(diǎn)的next
even = even->next;//更新偶數(shù)結(jié)點(diǎn)
}
odd->next = evenHead;//連接奇數(shù)和偶數(shù)兩個(gè)鏈表
return head;
}
(八)LeetCode143. 重排鏈表
題目描述
給定一個(gè)單鏈表 L:L0→L1→…→Ln-1→Ln
,
將其重新排列后變?yōu)椋?L0→Ln→L1→Ln-1→L2→Ln-2→…
思路
- 把整個(gè)鏈表劃分成2個(gè)等長的子鏈表,如果原鏈表長度為奇數(shù),那么第一個(gè)子鏈表的長度多1。
- 翻轉(zhuǎn)第二個(gè)子鏈表。
- 將兩個(gè)子鏈表合并。
代碼
ListNode * reorderList (ListNode* head)
{
if (!head || !head->next)
return head;
ListNode * fast = head;
ListNode * slow = head;
while (fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
}
//利用快慢指針找到中心位置,翻轉(zhuǎn)后面鏈表
ListNode * mid = reverseList (slow->next);
slow->next = NULL;
//合并p和q,p的長度 = q的長度 | q的長度 +1
ListNode * p, *q;
p = head->next;
q = mid;
ListNode *t = head;
while (p && q)
{
t->next = q;
t = t->next;
q = q->next;
t->next = p;
t = t->next;
p = p->next;
}
return head;
}
ListNode * reverseList (ListNode * head)
{
if (!head || !head->next)
return head;
ListNode * pre = NULL;
ListNode * p = head;
while (p)
{
ListNode * tmp = p->next;
p->next = pre;
pre = p;
p = tmp;
}
return pre;
}
(九)LeetCode445. 兩數(shù)相加 II
題目描述
給定兩個(gè)非空鏈表來代表兩個(gè)非負(fù)整數(shù)。數(shù)字最高位位于鏈表開始位置。它們的每個(gè)節(jié)點(diǎn)只存儲(chǔ)單個(gè)數(shù)字。將這兩數(shù)相加會(huì)返回一個(gè)新的鏈表。
思路
簡單的大數(shù)相加,首先各自翻轉(zhuǎn),然后逐位想加,最后處理進(jìn)位,需要注意最高位進(jìn)位時(shí)需要new一個(gè)新的結(jié)點(diǎn),再翻轉(zhuǎn)回去即可。
代碼
ListNode* addTwoNumbers (ListNode* l1, ListNode* l2)
{
if (!l1 || !l2)
return !l1 ? l2 : l1;
//首先各自翻轉(zhuǎn)
l1 = reverseList (l1);
l2 = reverseList (l2);
ListNode * head = new ListNode (l1->val + l2->val);
ListNode * sum = head;
ListNode * p = l1->next;
ListNode * q = l2->next;
//然后逐位相加
while (p || q)//
{
ListNode * tmp = new ListNode (0);
tmp->val = (p ? p->val : 0) + (q ? q->val : 0);
sum->next = tmp;
sum = sum->next;
p = p ? p->next : NULL;
q = q ? q->next : NULL;
}
sum->next = NULL;
//最后處理進(jìn)位
p = head;
while (p->next)
{
if (p->val >= 10)
{
p->val -= 10;
p->next->val += 1;
}
p = p->next;
}
if (p->val >= 10) //最后一位數(shù)需要進(jìn)位
{
p->val -= 10;
ListNode * tmp = new ListNode (1);
p->next = tmp;
p = p->next;
p->next = NULL;
}
//翻轉(zhuǎn)回去
head = reverseList (head);
return head;
}
ListNode * reverseList (ListNode * head)
{
if (!head || !head->next)
return head;
ListNode * pre = NULL;
ListNode * p = head;
while (p)
{
ListNode * tmp = p->next;
p->next = pre;
pre = p;
p = tmp;
}
return pre;
}
(十)LeetCode725. Split Linked List in Parts
題目描述
要求將鏈表進(jìn)行分割,使分割的部分盡可能一樣,如果不是平均分,應(yīng)使任意部分大小不相差1,如果要求的分割段數(shù)小于鏈表的元素?cái)?shù),則末尾用nullptr填充。
思路
求出鏈表的長度,對k求除數(shù)和余數(shù),商res代表分割的長度,余數(shù)mod表示前mod分割塊的長度要+1。
代碼
vector<ListNode*> splitListToParts (ListNode* root, int k)
{
vector<ListNode*> vec (k, nullptr);
if (!root)
return vec;
ListNode * p = root;
int len = 0;
while (p)
{
len++;
p = p->next;
}
int res = len / k;
int mod = len % k;
p = root;
if (res == 0)//鏈表長度小于k
{
ListNode * nxt = p->next;
for (int i = 0; i < len; i++)
{
vec[i] = p;
nxt = p->next;
p->next = NULL;
p = nxt;
}
return vec;
}
for (int i = 0; i < k; i++)
{
int tmp = res + (i < mod ? 1 : 0);
vec[i] = p;
tmp--; //少走一步,方便保存下一個(gè)結(jié)點(diǎn)和將當(dāng)前部分的末尾置為空
while (tmp--)
{
p = p->next;
}
ListNode * nxt = p->next;
p->next = NULL;
p = nxt;
}
return vec;
}
(十一)LeetCode148. 排序鏈表
題目描述
在 O(n log n) 時(shí)間復(fù)雜度和常數(shù)級空間復(fù)雜度下,對鏈表進(jìn)行排序。
思路
鏈表歸并排序模板題。
代碼
ListNode* sortList (ListNode* head)
{
if (!head || !head->next)
return head;
ListNode * fast = head;
ListNode * slow = head;
ListNode * pre = NULL;
while (fast && fast->next)
{
pre = slow;
fast = fast->next->next;
slow = slow->next;
}
pre->next = NULL;
return mergeTwoLists (sortList (head), sortList (slow));
}
ListNode* mergeTwoLists (ListNode* l1, ListNode* l2)
{
if (l1 == NULL || l2 == NULL)
{
return l1 == NULL ? l2 : l1;
}
ListNode * head = new ListNode (0);
ListNode * p = head;
while (l1 && l2)
{
if (l1->val <= l2->val)
{
p->next = l1;
l1 = l1->next;
}
else
{
p->next = l2;
l2 = l2->next;
}
p = p->next;
}
p->next = l1 == NULL ? l2 : l1;
return head->next;
}
(十二)LeetCode817. Linked List Components
題目描述
給出一個(gè)鏈表和一個(gè)數(shù)組,尋找有幾個(gè)component, 每一個(gè)component的val必須在數(shù)組G中而且在鏈表中是連接的。
思路
遍歷鏈表,使用tar變量來標(biāo)記,初始化為0,查看鏈表中當(dāng)前元素在是否在集合中
- 假如不在集合中,如果tar為0,表示前面沒有找到或者已經(jīng)處理了;tar為1則表示前面的component結(jié)束了,則res++,并將tar置為0;
- 假如在集合中,將tar置為1即可。
代碼
int numComponents (ListNode* head, vector<int>& G)
{
if (!head || !G.size())
return 0;
set<int > S;
for (int i = 0; i < G.size(); i++)
S.insert (G[i]);
ListNode * p = head;
int tar = 0;
int res = 0;
while (p)
{
if (S.find (p->val) != S.end()) //查找成功
tar = 1;
else
{
if (tar)
{
tar = 0;
res++;
}
}
p = p->next;
}
if (tar == 1)
res++;
return res;
}
(十三)LeetCode147. 對鏈表進(jìn)行插入排序
題目描述
對鏈表進(jìn)行插入排序。
思路
直接模擬插入排序過程即可
代碼
ListNode* insertionSortList (ListNode* head)
{
if (!head || !head->next)
return head;
ListNode * q = head->next;//q表示當(dāng)前需要排序的元素
ListNode * qPre = head;//表示已經(jīng)排好序的最后一個(gè)元素,也就是q的前一個(gè)元素
while (q)
{
ListNode * p = head, *pPre = NULL;//pPre表示p的前一個(gè)元素
while (p != q)
{
if (p->val > q->val)//需要將q插入到p前面
{
ListNode * tmp = q->next;//保存q的next信息
if (!pPre)//q作為頭結(jié)點(diǎn)
head = q;
else//p的前一個(gè)元素指向q
pPre->next = q;
q->next = p;//q指向p
qPre->next = tmp;//已經(jīng)排好序的最后一個(gè)元素指向tmp(原來q的next)
q = qPre;//恢復(fù)位置,繼續(xù)循環(huán)
break;
}
pPre = p;
p = p->next;
}
qPre = q;
q = q->next;
}
return head;
}
(十四)LeetCode86. 分隔鏈表
題目描述
給定一個(gè)鏈表和一個(gè)特定值 x,對鏈表進(jìn)行分隔,使得所有小于 x 的節(jié)點(diǎn)都在大于或等于 x 的節(jié)點(diǎn)之前。
應(yīng)當(dāng)保留兩個(gè)分區(qū)中每個(gè)節(jié)點(diǎn)的初始相對位置。
輸入: head = 1->4->3->2->5->2, x = 3
輸出: 1->2->2->4->3->5
思路
分別用兩個(gè)鏈表記錄在左邊還是右邊,最后將左右兩個(gè)鏈表合并即可。
代碼
ListNode* partition (ListNode* head, int x)
{
if (!head || !head->next)
return head;
ListNode * leftHead = NULL, * rightHead = NULL;
ListNode * left = NULL, * right = NULL;
ListNode * p = head;
while (p)
{
if (p->val < x)
{
if (!left)
{
left = p;
leftHead = p;
}
else
{
left->next = p;
left = left->next;
}
}
else
{
if (!right)
{
right = p;
rightHead = p;
}
else
{
right->next = p;
right = right->next;
}
}
p = p->next;
}
if (!right)
{
left->next = NULL;
return leftHead;
}
right->next = NULL;
if (!left)
return rightHead;
left->next = rightHead;
return leftHead;
}
(十五)LeetCode92. 反轉(zhuǎn)鏈表 II&LeetCode25. k個(gè)一組翻轉(zhuǎn)鏈表
題目描述
- 反轉(zhuǎn)從位置 m 到 n 的鏈表。請使用一趟掃描完成反轉(zhuǎn)。
- 給出一個(gè)鏈表,每 k 個(gè)節(jié)點(diǎn)一組進(jìn)行翻轉(zhuǎn),并返回翻轉(zhuǎn)后的鏈表。
k 是一個(gè)正整數(shù),它的值小于或等于鏈表的長度。如果節(jié)點(diǎn)總數(shù)不是 k 的整數(shù)倍,那么將最后剩余節(jié)點(diǎn)保持原有順序。
思路
- 處理好細(xì)節(jié)即可,將需要反轉(zhuǎn)的區(qū)間斷開,將斷開出的結(jié)點(diǎn)信息保存下來,然后反轉(zhuǎn)對應(yīng)的區(qū)間,最后在將該區(qū)間插入到原來的位置
- 第二題需要用到第一題的解法,即每k個(gè)反轉(zhuǎn)一次,轉(zhuǎn)化成對應(yīng)區(qū)間的反轉(zhuǎn),調(diào)用第一題的代碼即可
代碼
ListNode* reverseKGroup (ListNode* head, int k)//K個(gè)一組反轉(zhuǎn)鏈表
{
if (!head || !head->next)
return head;
ListNode * p = head;
int len = 0;
while (p)
{
len++;
p = p->next;
}
if (k > len)
return head;
for (int i = 1; i + k - 1 <= len; i += k)
{
//反轉(zhuǎn)從位置 i 到 i+k-1 的鏈表
head = reverseBetween (head, i, i + k - 1);
}
return head;
}
ListNode* reverseBetween (ListNode* head, int m, int n)//反轉(zhuǎn)從位置 m 到 n 的鏈表
{
if (!head || !head->next)
return head;
int cnt = 1;
ListNode * p = head, * pre = NULL;
//分別表示翻轉(zhuǎn)前面的最后一個(gè)結(jié)點(diǎn),翻轉(zhuǎn)后面的第一個(gè)結(jié)點(diǎn),和需要翻轉(zhuǎn)的第一個(gè)結(jié)點(diǎn)
ListNode * preTail = NULL, * nextHead = NULL, * revHead = NULL;
while (p)
{
if (cnt == m)
{
preTail = pre;//保存翻轉(zhuǎn)前的最后一個(gè)結(jié)點(diǎn)
revHead = p;//從該結(jié)點(diǎn)開始翻轉(zhuǎn)
}
if (cnt == n)
{
nextHead = p->next;//保存翻轉(zhuǎn)后面的第一個(gè)結(jié)點(diǎn)
p->next = NULL;//使得翻轉(zhuǎn)的部分隔開
break;
}
pre = p;
p = p->next;
cnt++;
}
revHead = reverseList (revHead);
p = revHead;
while (p->next)//找到翻轉(zhuǎn)部分最后一個(gè)元素
p = p->next;
if (!preTail)//從第一個(gè)元素翻轉(zhuǎn)則需更新頭結(jié)點(diǎn)
head = revHead;
else
preTail->next = revHead;
p->next = nextHead;
return head;
}
ListNode * reverseList (ListNode * head)//反轉(zhuǎn)鏈表
{
if (!head || !head->next)
return head;
ListNode * pre = NULL;
ListNode * p = head;
while (p)
{
ListNode * tmp = p->next;
p->next = pre;
pre = p;
p = tmp;
}
return pre;
}
(十六)LeetCode61. 旋轉(zhuǎn)鏈表
題目描述
給定一個(gè)鏈表,旋轉(zhuǎn)鏈表,將鏈表每個(gè)節(jié)點(diǎn)向右移動(dòng) k 個(gè)位置,其中 k 是非負(fù)數(shù)。
思路
鏈表每個(gè)節(jié)點(diǎn)向右移動(dòng) k 個(gè)位置,可以理解為將鏈表前l(fā)en-k個(gè)元素放在了鏈表的末尾。
因?yàn)檫@樣對第一個(gè)結(jié)點(diǎn)來說,他現(xiàn)在的位置正好處于沒有移動(dòng)的k個(gè)元素后面。
代碼
ListNode* rotateRight (ListNode* head, int k)
{
if (!head || !head->next || !k)
return head;
int len = 0;
ListNode * p = head;
ListNode * tail = head;
while (tail->next)//找到末尾結(jié)點(diǎn)
{
len++;
tail = tail->next;
}
len++;
k = k % len;
if (!k)
return head;
k = len - k;//向右旋轉(zhuǎn)k步可以理解成將前l(fā)en-k個(gè)元素放到鏈表尾部
p = head;
int cnt = 1;
ListNode * newHead = NULL;
while (p)
{
if (cnt == k)
{
newHead = p->next;
p->next = NULL;
break;
}
cnt++;
p = p->next;
}
tail->next = head;
return newHead;
}
(十七)LeetCode82. 刪除排序鏈表中的重復(fù)元素 II&LeetCode19. 刪除鏈表的倒數(shù)第N個(gè)節(jié)點(diǎn)
題目描述
給定一個(gè)排序鏈表,刪除所有含有重復(fù)數(shù)字的節(jié)點(diǎn),只保留原始鏈表中 沒有重復(fù)出現(xiàn) 的數(shù)字。
思路
注意細(xì)節(jié),小心頭結(jié)點(diǎn)
刪除鏈表的倒數(shù)第N個(gè)節(jié)點(diǎn)時(shí),用兩個(gè)指針,第一個(gè)先走N步,然后一起走,第一個(gè)指針為空時(shí),第二個(gè)指針則為倒數(shù)第N個(gè)結(jié)點(diǎn)。
代碼
ListNode* deleteDuplicates (ListNode* head)
{
if (head == NULL || head->next == NULL)
return head;
ListNode * pre = NULL;
ListNode * p = head;
ListNode * next = NULL;
while (p)
{
bool flag = false;
next = p->next;
while (next && next->val == p->val)
{
flag = true;
next = next->next;
}
if (flag) //需要?jiǎng)h除
{
if (pre == NULL) //頭結(jié)點(diǎn)也需要?jiǎng)h除
head = next;
else
pre->next = next;
}
else//不需要?jiǎng)h除,更新pre
pre = p;
p = next;
}
return head;
}
ListNode* removeNthFromEnd (ListNode* head, int n)
{
if (!head)
return NULL;
ListNode * p1 = head;
while (n--)
p1 = p1->next;
if (!p1)
return head->next;
ListNode * p2 = head;
ListNode * pre = NULL;
while (p1 && p2)
{
p1 = p1->next;
pre = p2;
p2 = p2->next;
}
//刪除p2
pre->next = p2->next;
return head;
}
(十八)LeetCode138. 復(fù)制帶隨機(jī)指針的鏈表
題目描述
給定一個(gè)鏈表,每個(gè)節(jié)點(diǎn)包含一個(gè)額外增加的隨機(jī)指針,該指針可以指向鏈表中的任何節(jié)點(diǎn)或空節(jié)點(diǎn)。
要求返回這個(gè)鏈表的深度拷貝。
思路
可以用哈希表保存隨機(jī)指針,更簡單的解法是:
- 首先在每個(gè)結(jié)點(diǎn)后面復(fù)制一個(gè)當(dāng)前結(jié)點(diǎn),并將其插入到該結(jié)點(diǎn)后面。
- 更新復(fù)制結(jié)點(diǎn)的random信息,注意細(xì)節(jié)
- 將鏈表一分為二,即將原鏈表中的奇數(shù)結(jié)點(diǎn)和偶數(shù)結(jié)點(diǎn)構(gòu)成兩個(gè)鏈表,返回偶數(shù)結(jié)點(diǎn)構(gòu)成的鏈表即可。
代碼
RandomListNode *copyRandomList (RandomListNode *head)
{
if (!head)
return NULL;
RandomListNode * p = head;
while (p)//在每個(gè)結(jié)點(diǎn)后面復(fù)制一個(gè)當(dāng)前結(jié)點(diǎn)
{
RandomListNode * node = new RandomListNode (p->label);
node->next = p->next;
p->next = node;
p = node->next;
}
p = head;
while (p)//更新復(fù)制后的結(jié)點(diǎn)的random指針
{
if (p->random)
p->next->random = p->random->next;
p = p->next->next;
}
RandomListNode * newHead = head->next;
p = head;
while (p)//將鏈表一分為二
{
RandomListNode * tmp = p->next;
p->next = p->next ? p->next->next : NULL;
p = tmp;
}
return newHead;
}
(十九)LeetCode23. 合并K個(gè)排序鏈表
題目描述
合并 k 個(gè)排序鏈表,返回合并后的排序鏈表。請分析和描述算法的復(fù)雜度。
思路
常規(guī)解法
可以用歸并排序的思想。
首先將k個(gè)鏈表兩兩合并成k/2個(gè)新鏈表,然后將k/2個(gè)鏈表兩兩合并成k/4個(gè)新鏈表
如此繼續(xù)直到最終只有一個(gè)鏈表即為結(jié)果。
高效解法---優(yōu)先級隊(duì)列實(shí)現(xiàn)
優(yōu)先級隊(duì)列的優(yōu)先級情況:堆頂始終為最小的結(jié)點(diǎn),空結(jié)點(diǎn)在堆底
首先將所有頭結(jié)點(diǎn)壓入優(yōu)先級隊(duì)列,然后逐個(gè)彈出,插入新的鏈表,同時(shí)將彈出的結(jié)點(diǎn)的next壓入優(yōu)先級隊(duì)列
直到優(yōu)先級隊(duì)列為空集或者隊(duì)首元素為空則結(jié)束
代碼
//法一 常規(guī)解法
ListNode* mergeKLists (vector<ListNode*>& lists)
{
if (!lists.size())
return NULL;
int low=0,high=lists.size()-1;
while(high)
{
low=0;//low重置為0,繼續(xù)合并
while(low<high)
{
//合并后保存在low中
lists[low]=mergeTwoLists(lists[low],lists[high]);
low++;
high--;
}
}
return lists[0];
}
ListNode* mergeTwoLists (ListNode* l1, ListNode* l2)//合并兩個(gè)排序鏈表
{
if (l1 == NULL || l2 == NULL)
{
return l1 == NULL ? l2 : l1;
}
ListNode * head = new ListNode (0);
ListNode * p = head;
while (l1 && l2)
{
if (l1->val <= l2->val)
{
p->next = l1;
l1 = l1->next;
}
else
{
p->next = l2;
l2 = l2->next;
}
p = p->next;
}
p->next = l1 == NULL ? l2 : l1;
return head->next;
}
//法二 優(yōu)先級隊(duì)列實(shí)現(xiàn)
struct cmp //自定義優(yōu)先級
{
bool operator () (ListNode * l1, ListNode * l2) // 重載括號
{
if (!l1 || !l2)
return !l1;//NULL置于堆底
return l1->val > l2->val; // 等同于greater, 堆頂元素是最小值
}
};
ListNode* mergeKLists (vector<ListNode*>& lists)
{
if (!lists.size())
return NULL;
priority_queue<ListNode *, vector<ListNode * >, cmp > Q; //優(yōu)先級隊(duì)列
for (int i = 0; i < lists.size(); i++) //將每個(gè)序列的頭結(jié)點(diǎn)插入優(yōu)先級隊(duì)列
Q.push (lists[i]);
ListNode * head = new ListNode (-1); //偽頭結(jié)點(diǎn)
ListNode * p = head;
while (!Q.empty() && Q.top())
{
ListNode * tmp = Q.top();
Q.pop();
p->next = tmp;
p = p->next;
Q.push (tmp->next);
}
return head->next;
}