一文搞懂单链表的六大解题套路
//合并2个升序链表
ListNode mergeTwoLists(ListNode l1, ListNode l2) {
//虚拟头节点 dummy是用来定位合并后的到的链表 怕丢失
ListNode dummy = new ListNode(-1), p = dummy;
ListNode p1 = l1, p2 = l2;
while (p1 != nullptr && p2 != nullptr) {
//比较p1和p2两个指针,将小的节点连接到p指针
if (p1->val > p2.val) {
p->next = p2;
p2 = p2->next;
}else {
p->next = p1;
p1 = p1->next;
}
//p指针不断前进!!!别忘记了
p = p->next;
}
if (p1 != nullptr)
p.next = p1;
if (p2 != nullptr)
p.next = p2;
return dummy.next;
}
//######
//合并k个升序链表
//方法一 小根堆,全部入堆
LinkNode* mergeKLists(vector<ListNode*>& lists) {
if (lists.size() == 0)
return NULL;
//小根堆
priority_queue<int, vector<int>, greater<int> > smallQ;
for (ListNode* x : lists) {
while (x) {
smallQ.push(x->val);
x = x->next;
}
}
ListNode* hair = new ListNode(-1);
ListNode* cur = hair;
while (!smallQ.empty()) {
int val = smallQ.top();
smallQ.pop();
cur->next = new ListNode(val);
cur = cur->next;
}
return hair->next;
}
//时间复杂度是 O(Nlogk),其中 k 是链表的条数,N 是这些链表的节点总数
//######
//单链表的倒数第k个节点
//双指针
ListNode findFromEnd(ListNode head, int k) {
ListNode p1 = head;
//先走k步
for (int i = 0; i < k; i++) {
p1 = p1->next;
}
//一起走n-k步
while (p1 != NULL) {
p1 = p1->next;
p2 = p2->next;
}
return p2;
}
//######
//删除单链表的倒数第n个节点
LinkNode removeNthFromEnd(LinkNode head, int n) {
//虚拟头节点 防止找不动删除后的链表
ListNode dummy = new ListNode(-1);
dummy->=next = head;
//删除倒数第n个,要先找到倒数第n+1个
ListNode p = findFromeEnde(dummy, n + 1);
//删除倒数第n个
p->next = (p->next)->next;
return dummy->next;
}
LisrNode findFromEnd(LinkNode head, int k) {
LinkNode p1 = head;
for (int i = 0; i < k; i++) {
p1 = p1->next;
}
LinkNode p2 = head;
while (p2 != nullptr) {
p1 = p1->next;
p2 = p2->next;
}
return p2;
}
//######
//单链表的中点
//快慢指针
//(如果链表长度为偶数,也就是说中点有两个的时候,我们这个解法返回的节点是靠后的那个节点)
LinkNode mindleNode(LinkNode head) {
LinkNode slow = head, fast = head;
while (fast != nullptr && fast->next != nullptr) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
//######
//判断链表中是否包含环
bool hasCycle(LinkNode head) {
LinkNode slow = head, fast = head;
while (fast != nullptr && fast->next != nullptr) {
slow = slow->next;
fast = fast->next->next;
//快慢指针相遇,说明含有环
if (slow == fast)
return true;
}
return false;
}
//如果链表中含有环,如何计算这个环的起点?
LinkNode detectCycle(ListNode head) {
LinkNode slow = head, fast = head;
while (fast != nullptr && fast->next != nullptr) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast)
break;
}
if (fast == nullptr || fast->next == nullptr)
//fast遇到
return nullptr;
//重新指向头结点
slow = head;
//快慢指针同步前进,相交点就是环起点
while (slow != fast) {
slow = slow->next;
fast = fast->next;
}
return slow;
}
//######
//判断两个链表是否相交
//方法一:让 p1 遍历完链表 A 之后开始遍历链表 B,让 p2 遍历完链表 B 之后开始遍历链表 A,
//这样相当于「逻辑上」两条链表接在了一起。
//如果这样进行拼接,就可以让 p1 和 p2 同时进入公共部分,也就是同时到达相交节点 c1:
ListkNode getIntersectionNode(ListNode headA, LinkNode headB) {
ListNode p1 = headA, p2 = headB;
while (p1 != p2) {
if (p1 == nullptr)
p1 = headB;
else
p1 = p1->next;
if (p2 == nullptr)
p2 = headA;
else
p2 = p2->next;
}
return p1;
}
//方法二:如果把两条链表首尾相连,
//那么「寻找两条链表的交点」的问题转换成了前面讲的「寻找环起点」的问题
//方法三:可以通过预先计算两条链表的长度
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int lenA = 0, lenB = 0;
// 计算两条链表的长度
for (ListNode p1 = headA; p1 != null; p1 = p1.next) {
lenA++;
}
for (ListNode p2 = headB; p2 != null; p2 = p2.next) {
lenB++;
}
// 让 p1 和 p2 到达尾部的距离相同
ListNode p1 = headA, p2 = headB;
if (lenA > lenB) {
for (int i = 0; i < lenA - lenB; i++) {
p1 = p1.next;
}
}
else {
for (int i = 0; i < lenB - lenA; i++) {
p2 = p2.next;
}
}
// 看两个指针是否会相同,p1 == p2 时有两种情况:
// 1、要么是两条链表不相交,他俩同时走到尾部空指针
// 2、要么是两条链表相交,他俩走到两条链表的相交点
while (p1 != p2) {
p1 = p1.next;
p2 = p2.next;
}
return p1;
}