双指针算法之快慢指针(一):力扣【判断链表是否有环】leetcode-141、142

一、简介:什么是快慢指针?

快慢指针,顾名思义,无非就是设置一个快指针,一个慢指针,初始化的时候,快指针和慢指针都指向链表的头结点,前进的时候一个在前一个在后,结合起来可以十分巧妙的解决链表中的一些问题;

看完本文可以解决leetcode141题(简单)以及leetcode142题(中等)。
双指针算法之快慢指针(二):力扣【寻找链表的第N个点】leetcode-876、19

二、典例一:判断链表是否有环

这个应该属于链表算法的经典的操作了,如果一个单链表有环,一个指针是判断不出来的,因为链表尾部和头部"链接"在一起,形成了一个死循环。

boolean hasCycle(ListNode head) {
    
    
    while (head != NULL)
        head = head.next;
    return false;
}

如果使用一个指针:上述的代码,如果没有环的话,head 指针最后就会走到NULL,就会返回 false。到那时如果链表是有环的,就麻烦了,head会在循环中一直出不来的,自然不会有结果~
但是,如果设置快慢两个指针,快指针走到null的时候,就可以得知这个链表是没有环的;如果快慢指针相遇了,说明快指针肯定已经走了几圈了,这个链表肯定是有环的。
比较经典的就是leetcode的141题

1、题目描述:

在这里插入图片描述
在这里插入图片描述

2、思路以及代码

只要快慢指针相遇,说明是有环的,所以我们这里设置快指针 fast 一次走两步,慢指针 slow一次就一步;

/**
 * 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 *fast,*slow;
        fast = slow = head;  // 初始都头节点的位置
        while (fast != NULL && fast->next != NULL) {
    
    
            fast = fast->next->next;
            slow = slow->next;
            if (fast == slow) {
    
      // 相遇了
                return true;
            }
        }
        return false;
    }
};

三、典例二:判断链表中是否有环,有环就返回环的起始位置

刚刚的快慢指针判断链表是不是有环其实还是很好理解的,但是进一步提升的话,判断出来有环之后,返回这个环的开始位置, 不仅仅需要判断是不是有环,有环的话还有知道环是where 开始的。

比较经典的就是leetcode的142题

1、题目描述

在这里插入图片描述

2、题目分析

在这里,我们先回到上一题的解法,使用快慢指针去判断是不是有环,如果快慢指针相遇了,就是有环,那么,快慢指针相遇在哪一个点呢?这个点有什么特别之处吗?除了相遇的点,我们还应该注意到链表头结点,相遇点,环开始结点。
其实142题的核心,确实就在于分析一下这3个点,下面我们就来分析一下:

下图的声明:
O 是链表头结点
A 是环开始的点
B 是快指针和满指针相遇的点
K 是慢指针 slow 在快慢指针相遇之时走的路程,即推:快指针是 2 K
L(OA)是 从O 开始 第一次遇到 A 走的路长,就是下图蓝色部分
L(AB)是 从A 开始 第一次遇到 B 走的路长,就是下图红色部分
L环 是 从A 开始 再一次遇到 A 走的路长 其实就是k了
L(BA)是 从B开始 第一次遇到 A 走的路长。就是下图绿色部分

根据下图的计算,我们可以知道 L(OA)和 L(BA) 的长度是一样的
在这里插入图片描述

由于 L(OA)和 L(BA) 的长度是一样的,所以我们在快指针 fast 和满指针 slow 相遇的时候,在B点相遇的时候,

1、快指针 fast 继续前进,速度和慢指针一样
2、慢指针 slow 返回链表头结点 O点,
3、快慢指针的速度保持一致,各自继续前进

那么,快慢指针下一次相遇的点是哪里?没错,就是 A 点,就是环开始的结点。

3、解题代码

由此,我们可以写出下面的伪代码:

ListNode detectCycle(ListNode head) {
    
    
	快慢指针初始化,都在头结点
    while (fast != null && fast.next != null) {
    
    
    快指针一次两步
    慢指针一次一步
    	if (快指针 = 慢指针)  {
    
    //即相遇  此时在 B 点
    	    慢指针回到头结点;
   			 while (slow != fast) {
    
    
    	 		快慢指针继续走,一次一步;
   			 }
   		 	return slow;
   		 }
    }
    return NULL;
}

随后就可以写出全部的代码了:

class Solution {
    
    
public:
    ListNode *detectCycle(ListNode *head) {
    
    
        ListNode *fast,*slow;
        fast = slow = head;
        while (fast != NULL && fast->next != NULL) {
    
    
            fast = fast->next->next;
            slow = slow->next;
            if (fast == slow) {
    
    
                slow = head;
                while (fast != slow) {
    
    
                    fast = fast->next;    
                    slow = slow->next;
                }
                    return slow;
            }
 
        }
        return NULL;
    }
};

猜你喜欢

转载自blog.csdn.net/qq_43473694/article/details/109692641
今日推荐