链表六大解题套路(快慢双指针大法)

本文介绍了多种链表操作的算法实现,包括合并两个有序链表、合并K个升序链表、删除链表倒数第N个节点、找到链表的中间节点、判断环形链表、找到环的起点以及寻找链表的相交节点。通过这些实例展示了如何利用快慢指针、优先队列等技巧解决链表问题。
摘要由CSDN通过智能技术生成

1、合并两个有序链表(leetcode 21 简单)
 

class Solution {
    public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode* head = new ListNode(-1);
        ListNode* p = head;
        
        while(list1 && list2)
        {
            if(list1->val < list2->val)
            {
                p->next = list1;
                list1 = list1->next;
            }
            else{
                p->next = list2;
                list2 = list2->next;
            }
            
            p = p->next;
        } 
        
        p->next = list1 == nullptr ? list2 : list1;
        
        return head->next;
    }
};

2、合并K个升序链表(leetcode 23 困难)

技巧:使用优先队列,把链表节点放入一个最小堆,就可以每次获得k节点中的最小节点

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
       ListNode* head = new ListNode(-1);
       ListNode* p = head;

       while(list1 && list2)
       {
           if(list1->val < list2->val)
           {
               p->next = list1;
               list1 = list1->next;
           }
           else{
               p->next = list2;
               list2 = list2->next;
           }

           p = p->next;
       } 

       p->next = list1 == nullptr ? list2 : list1;

       return head->next;
    }
};

3、删除链表的倒数第 N 个结点(leetcode 19 中等)

技巧:使用快慢指针p1, p2,首先让p1先走k步,然后p2跟p1一起移动,直到p1走到最后一个节点,那么p2指向了倒k节点(要删除N节点,那么p2只要先走n+1步)

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
       ListNode* head = new ListNode(-1);
       ListNode* p = head;

       while(list1 && list2)
       {
           if(list1->val < list2->val)
           {
               p->next = list1;
               list1 = list1->next;
           }
           else{
               p->next = list2;
               list2 = list2->next;
           }

           p = p->next;
       } 

       p->next = list1 == nullptr ? list2 : list1;

       return head->next;
    }
};

4、链表的中间结点(leetcode 876 简单)

技巧:使用快慢节点low、fast,fast每次走两步,low每次走一步,当fast走到最后一个节点,那么low就是中间节点(注意:如果链表长度是偶数,low指向的右边那个节点)

/**
 * Definition for singly-linked list.
 * 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* middleNode(ListNode* head) {
        ListNode* low = head, *fast = head;

        while(fast && fast->next != nullptr)
        {
            low = low->next;
            fast = fast->next->next;
        }

        return low;
    }
};

5、判断是否环形链表(leetcode 141 简单)

技巧:使用快慢指针fast、low,fast每走两步,low走一步,如果快慢指针相遇(fast==low),那么就是有环;否则如果fast变为空指针,则无环

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
       ListNode *low = head, *fast = head;

       while(fast && fast->next != nullptr)
       {
           low = low->next;
           fast = fast->next->next;

           if(low == fast)
                return true;//break; //有环
       } 

       return false;
    }
};

6、找出环形链表环的起点 II(leetcode 142 中等)

技巧:使用快慢指针fast、low,让fast每走两步,low走一步,如果快指针变成了空指针,那么无环;如果快慢指针相遇(fast == low),那么有环,此时让慢指针重新指向头结点(low=head),快慢指针一起往前走,下次相遇就是环的起点(如图解释):

如果有环,那么到达相遇点low指针走了k步,fast指针走了2k步,多走的部分在环内转圈圈(可得k步为圈大小的整数倍);假设环起点到相遇点为m步,那么相遇点到环起点就是k-m步,又因为head到环起点也是k-m步,所以快慢指针再走k-m步就会在环起点相遇

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
       ListNode *low = head, *fast = head;

       while(fast && fast->next != nullptr)
       {
           low = low->next;
           fast = fast->next->next;

           if(low == fast)
                break; //有环
       } 

       if(fast == nullptr || fast->next == nullptr)
       {
           return nullptr;
       }

       low = head;
       while(low != fast){
           low = low->next;
           fast = fast->next;
       }

       return low;
    }
};

7、 相交链表(leetcode 160 简单)

技巧1:使用hastmap记录第一链表的所有节点,让后遍历另一个链表,如果可以在hash中找到则表明他们相交

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
       unordered_map<ListNode*,bool> hashmap;

       ListNode* p = headA;
       while(p)
       {
            hashmap[p] = true;
            p = p->next;
       } 

       p = headB;
       while(p)
       {
           if(hashmap[p]){
               return p;
           }

           p = p->next;
       }

       return nullptr;
    }
};

进阶,技巧2:假设两个链L1,L2表相交,我们把L1独立部分长度设为len1, L2独立部分长度设为len2, 相交公共部分长度设为len3, 由于等式: len1 + len3 + len2 = len2 + len3 + len1 =>> L1长度(len1 + len3) + L2独立部分(len2) = L2长度(len2 + len3) + L1独立部分len1 ,那么只要使用两个指针p1,p2分别走完上面的路径(p1指向L1,如果遇到nullptr那么指向L2,p2指向L2,如果遇到nullptr那么指向L1),相遇的时候就是交点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *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;
    }
};

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值