LeetCode160 & LeetCode141——double pointers to solve the linked list

一次总结两道关于带环链表的算法题,这两道题在解法上都使用了快慢指针的基本思想。事实上,链表中含环路的问题常常和快慢指针解法脱不开关系,所以要建立这种思考反射。
首先是相交链表,顾名思义,这道题是判断两个链表是否从某一个节点之后开始走向统一,题面如下:
在这里插入图片描述
其实如果仔细分析的话,可以发现这道题目的难点就在于两个链表中如果真的存在相交点的话,它们在没有相交之前的部分长度是不统一的,而且我们并不确定这两者之间差多少,按同样运动速度运行的指针必然会错过这一交点。所以这道题的最终解法的思路出发点就是人为的拉平它们之间的路程差距,使得从两个链表头部分别出发的两枚运动速度相同的指针在相遇时(如果可以的话)走过的路程也相同,这样的话可以断定:如果两个链表确实相交,一定存在某个相交点,在这个相交点经过路程平衡的两个指针一定会相遇,这就是整个问题的解决思路。

那么如何人为拉平路程差距呢?方法就是:让从链表A头部开始运动的指针在走完链表A之后再开始从链表B的头部再次开始运动,反之同样。这样就补齐了两个链表的长度差距。如果两个链表不相交,那么可以断定的是在任一指针第二次到达其所在链表终点的时候如果还没相交,那么可以跳出循环并断言查找失败,因为要真有交点的话之前已经相交了。

下面给出我写的代码,为了编码方便,我写了一个shiftTrack函数来帮助指针转换链表轨道,bool 返回值表示是否转换成功,其实也就是在判断是否查找失败。对于每一枚指针,均有一个pair来记录其信息,first元素记录的是其开始时从哪个链表头部开始的,0表示A链表,1表示B链表;second元素表示之前是否已经经过一次转换,这是判断是否查找失败的重要依据。

bool shiftTrack(ListNode*& Ptr, pair<bool, bool>& Flag, ListNode *headA, ListNode *headB)
{
    
    
    /*if the present pointer has not been on another track*/
    if(Flag.second == 0)
    {
    
    
        /*Ptr1*/
        if(Flag.first == 0)
            Ptr = headB;
        /*Ptr2*/
        else
            Ptr = headA;
        
        Flag.second = 1;
        return true;
    }
        
    return false;
}    

然后是主程序代码,其中使用了while true…break设计成例。

ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
    
    
        /*declare 2 pointers*/
        ListNode* Ptr1 = headA;
        ListNode* Ptr2 = headB;

        /*success flag*/
        bool Success = false;

        /*the first element indicating which one the pointer is*/
        /*the second element indicating whether the pointer has been on another track*/
        pair<bool, bool> Flag1 = make_pair(0, 0);
        pair<bool, bool> Flag2 = make_pair(1, 0);

        while(true)
        {
    
    
            /*if the intersection point is founded, jump out of loop*/
            if(Ptr1 == Ptr2)
            {
    
    
                Success = true;
                break;
            }
            else
            {
    
    
                /*modify the pointer to next position*/
                if(Ptr1->next != nullptr)
                    Ptr1 = Ptr1->next;
                else if(!shiftTrack(Ptr1, Flag1, headA, headB))
                    break;

                if(Ptr2->next != nullptr)
                    Ptr2 = Ptr2->next;
                else if(!shiftTrack(Ptr2, Flag2, headA, headB))
                    break;
            }
        }

        if(Success)
            return Ptr1;
        else    
            return nullptr;
    }

这就是一道使用快慢指针算法解决两链表相交问题的概述。与之类似,还有下面一道题,这道题并不涉及两个链表,而是判断一个链表内部是否有环路
在这里插入图片描述
这道题也是使用两个指针,不同的是人为地让这两个指针以不同的运动速率进行运动。原理很简单,就像是在学校的环形操场上跑步,俩人一个跑的快一个跑得慢,这样跑得快的人会反复的追上跑得慢的人,这也就从侧面证明了跑道是环形的,否则不可能追上。

所以这道题的重点就是,让两个指针以不同速度跑下去,如果存在自环的话,快指针一定会追上慢指针,相遇时即可断言链表中存在环路,如果走到了链表的终点还没有自环出现,那就是没有自环了。首先是实现向前一步走的函数oneStepForward:

bool oneStepForward(ListNode*& Ptr)
{
    
    
    /*if the next position is null, return false*/
    if(Ptr == nullptr || Ptr->next == nullptr)
        return false;

    /*switch to the next position*/
    Ptr = Ptr->next;
    return true;
}

然后是向前两步走的函数twoStepForward,借由oneStepForward来实现:

bool twoStepForward(ListNode*& Ptr)
{
    
    
    /*implement the function through oneStepForward*/
    if(!oneStepForward(Ptr) || !oneStepForward(Ptr))
        return false;
    return true;
}

随后是主算法的逻辑实现:

bool hasCycle(ListNode *head) {
    
    
    /*if the list is null...*/
    if(head == nullptr)
        return false;

    /*declare 2 pointers here, let 1 pointer chase another 1 */
    ListNode* Fast = head->next;
    ListNode* Slow = head;

    while(true)
    {
    
    
        if(Fast != Slow)
            if(!oneStepForward(Slow) || !twoStepForward(Fast))
                return false;
            else continue;
        else
            return true;
    }
}

Guess you like

Origin blog.csdn.net/zzy980511/article/details/118196156