算法面试通关40讲 - 链表
三分学 七分练
leetcode 21 合并两个有序链表
尾插法 头结点
#include <iostream>
using namespace std;
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
class Solution {
public:
ListNode *mergeTwoLists(ListNode *list1, ListNode *list2) {
if (list1 == nullptr || list2 == nullptr) {
return list1 == nullptr ? list2 : list1;
}
// tail insert
ListNode header;
ListNode *tail = &header;
ListNode *tmp;
while (list1 != nullptr && list2 != nullptr) {
if (list1->val > list2->val) {
tmp = list1;
list1 = list2;
list2 = tmp;
}
tail->next = list1;
while (list1 != nullptr && list1->val <= list2->val) {
tmp = list1;
list1 = list1->next;
}
tail = tmp;
}
if (list1 == nullptr) {
tmp->next = list2;
}
return header.next;
}
};
int main() {
ListNode list1_4(4);
ListNode list1_2(2, &list1_4);
ListNode list1_1(1, &list1_2);
ListNode list2_4(4);
ListNode list2_3(3, &list2_4);
ListNode list2_1(1, &list2_3);
Solution sol;
ListNode *result = sol.mergeTwoLists(&list1_1, &list2_1);
}
leetcode 234 回文链表
双指针 快慢指针 头插法
class Solution {
public:
bool isPalindrome(ListNode *head) {
ListNode *slow = head;
ListNode *fast = head;
// 1
// ^
// 1 2 3 4 5 6 7
// ^ ^
// 1 2 3 4 5 6 7 8
// ^ ^
ListNode *tmp;
while ((tmp = fast->next) != nullptr && (fast = tmp->next) != nullptr) {
slow = slow->next;
}
// 1 2 3 4 5 6 7
// ^ ^
fast = slow->next;
slow->next = nullptr;
while (fast != nullptr) {
tmp = fast->next;
fast->next = slow->next;
slow->next = fast;
fast = tmp;
}
fast = slow->next;
while (fast != nullptr) {
if (fast->val != head->val) return false;
fast = fast->next;
head = head->next;
}
return true;
}
};
int main() {
Solution sol;
ListNode right1(1);
ListNode right2(2, &right1);
ListNode left2(2, &right2);
ListNode left1(1, &left2);
cout << sol.isPalindrome(&left1);
}
leetcode 142 环形链表 II
双指针 快慢指针
解析:假设快慢指针相逢的时候,慢指针走了\(slow = a + x\)(\(a\)是圈外长度,\(x\)是慢指针圈内走过的节点数目),
快指针走了\(fast = 2 slow = a + y\)(\(y\)是快指针圈内走过的节点数目),快指针比慢指针在圈内多走了\(fast - slow = slow = a + x = kb\)(\(k\)为正整数,\(b\)为圈长),
由于\(slow\)是线性递增的,必然存在一个最小的\(k\)使得\(slow = kb\);这里\(k\)未必等于\(1\),原因是\(slow\)走过单个\(b\)的长度未必能跨过\(a\)的长度走入圈中:\(a + x = kb\)。
\(slow + a = kb + a\)为慢指针转了正整数个圈走过的长度,所以只要在第一次快慢指针相遇的时候,让快指针重新回到起点与慢指针以相同速度前进,相遇点既是入口点。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *fast, *slow;
fast = slow = head;
while (fast != nullptr && (fast = fast->next) != nullptr) {
fast = fast->next;
slow = slow->next;
if (fast == slow) {
break;
}
}
if (fast == nullptr) return nullptr;
fast = head;
while (fast != slow) {
fast = fast->next;
slow = slow->next;
}
return fast;
}
};
leetcode 25 K 个一组翻转链表
头插法 头结点 链表截断/重连
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
// v before_begin v end
// -- 3 4 5
// 1 2
// ^ begin ^ tail
if (k == 1) return head;
ListNode header;
header.next = head;
ListNode *before_begin = &header;
ListNode *ori_begin, *begin, *tail, *end, *tmp;
while (true) {
begin = before_begin->next;
int kk = 0;
for (tail = begin; tail != nullptr && ++kk != k;) {
tail = tail->next;
}
// v before_begin v end
// -- 3 4 5
// 1 2
// ^ begin ^ tail ^ tmp
if (kk == k) {
end = tail->next;
tail = begin->next;
begin->next = end;
ori_begin = begin;
while (tail != end) {
tmp = tail->next;
tail->next = begin;
before_begin->next = tail;
begin = tail;
tail = tmp;
}
before_begin = ori_begin;
} else {
break;
}
}
return header.next;
}
};