Fast and slow pointers - extension of ideas on linked list oj problem

Table of contents

                                    1. Concept

2. Think: Why if the slow pointer takes one step and the fast pointer takes two steps, the fast pointer will definitely catch up?

3. Thinking: If the fast pointer takes 3 steps, 4 steps, 5 steps at a time... can't walk?

4. Thinking: Why does a pointer start from the position of the head pointer and visit backwards, and a pointer starts from the place where the fast and slow pointers meet, so why do the two pointers meet at the first node in the loop?

5.Examples


1. Concept

    Definition: Fast and slow pointers, that is, the slow pointer takes one step at a time, and the fast pointer takes two steps at a time. The two pointers start running from the actual position of the linked list. If the linked list has a ring, they will definitely meet in the ring, otherwise the fast pointer will reach the end of the linked list first. .

Note: The following nouns will be replaced by English in the following writings

      head pointer head

      fast pointer fast

      slow pointer slow

     The point where the fast and slow pointers meet meet

2. Think: Why if the slow pointer takes one step and the fast pointer takes two steps, the fast pointer will definitely catch up?

as the picture shows:

       Assume that the node distance between slow and fast after entering the loop is N. At this time, fast really starts to chase slow. Slow visits one node each time, and fast visits two nodes each time, so the distance between them each time It will be reduced by 1 node.

       That is, N N-1 N-2 N-3 ... 3 2 1 0 When the node distance between the two is 0, it means that the fast pointer has caught up with the slow pointer.

3. Thinking: If the fast pointer takes 3 steps, 4 steps, 5 steps at a time... can't walk?

as the picture shows:

       Suppose slow enters the ring, and the size of the ring is C. At this time, fast really starts to chase slow. Slow visits one node each time, and fast visits three nodes each time, so the distance between them will be reduced by two nodes each time. point.

       This will be discussed on a case-by-case basis:

N is an even number: N N-2 N-4 N-6 ... 6 4 2 0 At this time, fast catches up with slow.

N is an odd number: N N-2 N-4 N-6 ... 5 3 1 -1 When the node distance between the two is -1, it means that the distance between fast and slow is C - 1 .

At this time: In even numbers, you can basically catch up, but in odd numbers, you may not be able to catch up to a certain extent.

In summary, steps 3, 4...n all depend on the situation, and you may not be able to catch up, but you can definitely catch up in two steps.

4. Thinking: Why does a pointer start from the position of the head pointer and visit backwards, and a pointer starts from the place where the fast and slow pointers meet, so why do the two pointers meet at the first node in the loop?

as the picture shows:

      Assume that the distance between head and the first node visited into the ring is L, the size of the ring is C, the distance visited by slow into the ring is X, and the distance visited by fast is N.

Then  L = N*C - X

        After slow enters the ring, fast will definitely catch up with slow within one lap. Because during the pursuit, the distance between them will be reduced by 1 each time. After slow visits one circle of nodes, fast must have visited two circles of nodes, so the relative distance between them is one circle.

as the picture shows:

       And N*C - X is the node where the fast and slow pointers meet.

in conclusion:

       A pointer starts from the position of the head pointer and visits backward, and a pointer starts from the place where the fast and slow pointers meet. The two pointers will meet at the first node in the loop.

       Some friends may ask what the purpose of this is. Isn't this purely mathematical derivation? What does it have to do with logic? Let's take a look at the fourth question in the example. If we know this principle, the complexity of the code may be greatly reduced.

5.Examples

1. Find the intermediate node in the linked list. oj exercises

Idea: Define a set of fast and slow pointers, fast is 2 times slow. Both start from the address position of head and access backward at the same time. When fast accesses NULL backward, the position of slow is the intermediate node.

struct ListNode* middleNode(struct ListNode* head){
    
    struct ListNode* fase,*slow;

    fase = slow = head;

    while(fase && fase->next)//不清楚是奇数还是偶数
    {
        fase = fase->next->next;
        slow = slow->next;
    }
   
       return slow;
}

2. Find the Kth node from the last in the linked list. oj exercises

Idea: Define a set of fast and slow pointers, fast is 2 times slow. At this time, fast visits k nodes backward starting from the address position of head, and then visits them all backward. When fast accesses NULL, slow is the k-th node from the bottom.

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    // write code here
    if(pListHead == NULL)
    {
        return NULL;
    }

       struct ListNode* fase,*slow;
       fase = slow = pListHead;

       while(k--)//先让fase先走k步
       {
           if(fase == NULL)//如果fase走到空,说明链表没有k步长
           {
               return NULL;
           }

          fase=fase->next;
       }
        
       while(fase)
       {
           fase = fase->next;
           slow = slow->next;
       }

       return slow;

    }

3. Given a linked list, determine whether there is a cycle in the linked list. oj exercises

Idea: Define a set of fast and slow pointers, fast is 2 times slow. Both start from the address position of head and access backward at the same time. When the addresses of the two are equal, it proves that there is a cycle in the linked list. On the contrary, when fast accesses NULL, the addresses of the two are not equal, and there is no cycle.

bool hasCycle(struct ListNode *head) {
    if(head == NULL)
    {
        return false;
    }

    struct ListNode *fast, *slow;
    fast = slow = head;

    while(fast && fast->next)//不清楚是否带环,链表是否奇偶
    {
        fast = fast->next->next;
        slow = slow->next;

        if(slow == fast)
             return true;
    }

    return false;
    
}

4. Given the head node head of a linked list, return the first node where the linked list begins to enter the ring. If the linked list is loop-free, null is returned. oj exercises

Idea: Define a set of fast and slow pointers, fast is 2 times slow.

Method ①. Application 4. Think about  a pointer starting from the position of the head pointer and visiting backward, and a pointer starting from the place where the fast and slow pointers meet. The two pointers will meet at the first node of the loop.

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode *fast, *slow;
    fast = slow = head;

    while(fast && fast->next)//不清楚是否带环,链表是否奇偶
    {
        fast = fast->next->next;
        slow = slow->next;

        if(slow == fast)
        {
            struct ListNode* meet = slow;//此时slow == fast == meet
            while(meet != head)
            {
                meet = meet -> next;
                head = head ->next;
            }

            return meet;//此时meet == head 就是入环的第一个结点
        }
             
    }

    return NULL;  
}

Method ②. Create a pointer to visit backwards starting from the next node of the meet position, and find the intersection point of the two linked lists. (As shown below)

 

 There is a special case that needs to be considered here. What if meet == head?

struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode* fast, * slow;
    fast = slow = head;

    while (fast && fast->next)
    {
        //快慢指针同时向后开始访问
        fast = fast->next->next;
        slow = slow->next;

        //如果它们访问的地址相同,那么这个结点就是快慢指针相遇点
        if (slow == fast)
        {
            struct ListNode* meet = slow;

            if (head == meet)//如果head等于快慢指针相遇结点,就证明头指针就是入环的第一个结点
            {
                return head;
            }

            struct ListNode* curA = head, * curB = meet->next;

            int lenA = 1, lenB = 1; //求长度,从1开始,不然尾结点会漏掉


            while (curA->next != meet)
            {
                curA = curA->next;
                lenA++;
            }

            while (curB->next != meet)
            {
                curB = curB->next;
                lenB++;
            }

            //此时curA,curB都是尾结点

            //求第一个交点

             //假设A短B长
            struct ListNode* shortList = head, * longList = meet->next;
            //修正 如果A长B短,则交换彼此变量
            if (lenA > lenB)
            {
                shortList = meet->next;
                longList = head;
            }

            //差距
            int gap = abs(lenA - lenB);//abs求绝对值

            while (gap--) //长的先走差距步
            {
                longList = longList->next;
            }

            //同时走
            while (shortList != longList)
            {
                shortList = shortList->next;
                longList = longList->next;
            }


            //到这里肯定相等,返回 shortList / longList 都可以。

            return shortList;

        }
    }

    return NULL;//如果fast访问到NULL即证明无环
}

Guess you like

Origin blog.csdn.net/m0_73969113/article/details/131545793