一文搞懂单链表的六大解题套路

//合并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;

}

   

posted @ 2022-08-31 11:18  atomxing  阅读(29)  评论(0编辑  收藏  举报