题目: 给定一个链表, 删除链表的倒数第 k
个节点, 并返回链表的头节点.
示例:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
思路:
// 首先想到双指针技巧中的快慢指针.
// 1. 先让快指针走 k 步.
// 2. 再让快慢指针同时走 n-k 步.
// 3. 当快指针指向尾部时, 满指针指向倒数第 k 个节点.
代码:
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy = new ListNode(-1);
dummy->next = head;
ListNode *p1 = dummy, *p2 = dummy; // 首先想到双指针技巧中的快慢指针.
for (int i = 0; i < n; ++i) { // 1. 先让快指针走 k 步.
p1 = p1->next;
}
while (p1->next != nullptr) { // 2. 再让快慢指针同时走 n-k 步.
p1 = p1->next; // 3. 当快指针指向尾部时, 满指针指向倒数第 k 个节点.
p2 = p2->next;
}
ListNode *temp = nullptr;
temp = p2->next;
p2->next = p2->next->next;
temp->next = nullptr;
return dummy->next;
} // l r
}; // -1, 1, 2, 3 k=2
题目: 给定两个升序链表, 将其合并为一个升序链表并返回.
示例:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
思路:
// 1. 利用双指针, 每个指针指向一个链表.
// 2. 比较指针对应的值, 将小的节点指向大的节点.
// 3. 剩下的, 谁还没排完都接在最后面.
代码:
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* dummy = new ListNode(-1), * p = dummy;
ListNode* p1 = list1, * p2 = list2; // 1. 利用双指针, 每个指针指向一个链表.
while (p1 != nullptr && p2 != nullptr) {
if (p1->val < p2->val) { // 2. 比较指针对应的值, 将小的节点指向大的节点.
p->next = p1;
p1 = p1->next;
} else {
p->next = p2;
p2 = p2->next;
}
p = p->next;
}
if (p1 != nullptr) { // 3. 剩下的, 谁还没排完都接在最后面.
p->next = p1;
}
if (p2 != nullptr) {
p->next = p2;
}
return dummy->next;
} // 1 2
}; // [1, 3] [1, 4]
题目: 给定一个链表数组, 每个链表都是升序排列, 将这些链表合成一个升序链表.
示例:
输⼊:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到⼀个有序链表中得到。
1->1->2->3->4->4->5->6
思路:
// 构建一个优先队列,升序排列(小根堆).
// 每次比较, 每个链表都放进去一个.
// 当那个链表上的值为最小值时, 再添加新的元素进去.
代码:
class Solution {
ListNode* mergeKLists(vector<ListNode*> lists) {
struct {
bool operate()(ListNode* a, ListNode* b) {
return a->val > b->val;
}
};
priority_queue<ListNode*, vector<ListNode*>, comp> q;// 构建一个优先队列,升序排列(小根堆).
for (ListNode* node : lists) { // 每次比较, 每个链表都放进去一个.
if (node != nullptr) q.push(node);
}
ListNode* dummy = new ListNode(-1), * p = dummy;
while (!q.empty()) {
ListNode* node = q.top(); // 当那个链表上的值为最小值时, 再添加新的元素进去.
q.pop();
p->next = node;
p = p->next;
if (node->next != nullptr) q.push(node->next);
}
return dummy->next;
}
};
题目: 给定一个链表, 判断是否有环,有的话返回入环节点.
示例:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
思路:
// 判断有无环形链表, 想到快慢指针.
// 慢指针走一步, 快指针走两步.
// 当有环的话总会相遇.
// 同时将快指针指向dummy, 快慢指针同速运动, 相遇处为入环点.
// 示例:
// idx: -1, 0, 1, 2, 3 pos=1
// [-1,-1] [0,1] [1,3] [2,2] | [2,-1] [3,0] [1,1]
// idx: -1, 0, 1, 2, 3 pos=-1
// [-1,-1] [0,1] [1,3]
// if(fast=slow) return true; while(fast->next&&fast->next->next) return false
代码:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* dummy = new ListNode(-1);
dummy->next = head;
ListNode* fast = dummy, * slow = dummy;
while (fast->next && fast->next->next) { // 慢指针走一步, 快指针走两步.
slow = slow->next;
fast = fast->next->next;
if (fast == slow) {
fast = dummy;
while (fast != slow) {
slow = slow->next;
fast = fast->next;
}
return fast;
}
}
return nullptr;
}
};
题目: 给定两个头节点 headA
和 headB
, 判断连链表有无相交.
示例:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
思路:
定义两个指针 p1 和 p2, 分别指向 headA 和 headB.
p1 走完 A 链表后, 继续走 B 链表.
p2 走完 B 链表后, 继续走 A 链表.
若不相交, 则 p1==p2 为 nullptr 时停止.
若相交, 则 p1==p2 为相交点时停止.
代码:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *p1 = headA, *p2 = headB; // 定义两个指针 p1 和 p2, 分别指向 headA 和 headB.
while (p1 != p2) {
if (p1 == nullptr) p1 = headB; // p1 走完 A 链表后, 继续走 B 链表.
else p1 = p1->next;
if (p2 == nullptr) p2 = headA; // p2 走完 B 链表后, 继续走 A 链表.
else p2 = p2->next;
}
return p1; // 若不相交, 则 p1==p2 为 nullptr 时停止.
} // 若相交, 则 p1==p2 为相交点时停止.
};
题目: 给定一个链表, 判断该链表是否回文.
示例:
输入:head = [1,2,2,1]
输出:true
思路:
1. 找到链表的中间节点.
2. 反转后半部分链表.
3. 判断是否回文.
// pre cur next pre cur next
// null < head < node < node < node
// idx [0, 1, 2, 3, 4]
// [0,1][1,3] reutrn 2
代码:
bool isPalindrome(ListNode *head) {
ListNode *p1 = head, *p2 = head; // 1.找到链表的中间节点.
while (p2->next && p2->next->next) {
p1 = p1->next;
p2 = p2->next->next;
}
ListNode *left = head, *right = reverse(p1->next); //2. 反转后半部分链表.
while (right) {
if (left->val != right->val) return false;
left = left->next;
right = right->next;
}
p1->next = reverse(reHead);
return true;
}
/* 从 head 处反转链表 */
ListNode *reverse(ListNode *head) {
ListNode *pre = nullptr, *cur = head;
while (cur) {
ListNode *next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
return pre;
}
题目: 给定一个链表, 返回链表的中间节点.
示例:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
思路:
// 寻找链表的中间节点, 则想到快慢指针.
// 定义一个虚拟头节点 dummy->head.
// 慢指针走一步, 快指针走两步.
// 当快指针到头的时候, 慢指针于中间.
// 示例:
// idx: -1, 0, 1, 2, 3, 4
// [-1,-1] [0,1] [1,3]
// idx: -1, 0, 1, 2, 3
// [-1,-1] [0,1] [1,3]
// while(fast->next&&fast->next->next) return slow->next;
代码:
class Solution {
public:
ListNode* middleNode(ListNode* head) { // 寻找链表的中间节点, 则想到快慢指针.
ListNode* dummy = new ListNode(-1); // 定义一个虚拟头节点 dummy->head.
dummy->next = head;
ListNode* slow = dummy, * fast = dummy;
while (fast->next && fast->next->next) { // 慢指针走一步, 快指针走两步.
slow = slow->next; // 当快指针到头的时候, 慢指针于中间.
fast = fast->next->next;
}
return slow->next;
}
};