一个浪漫的算法(快慢指针)

伟人说:“对的人值得等,错的人不必等。只要是对的人,兜兜转转终是你。”

点击这里听一路向北 

 

 

原题目链接: 141. 环形链表

解析:

如何判断一个链表是否有环?采用快慢指正针做法最为便捷,那么什么是快慢指针呢?这其实是一个浪漫的算法。

情况一:

假设有 3,2,0,4 四个节点,我们可以把它们想象成四个城市,然后有个男生叫拓海,有个女生叫夏树,拓海在3城市这个地方,而夏树在2城市这个地方,他们都有各自的生活轨迹,谁也不认识谁。

他们的生活总是向前的,只不过拓海生活节奏比较慢,每次只前进一次,夏树生活节奏快,每次前进两次。

第一天之后变成

第二天之后变成

此时,他们在0城市相遇了,彼此一件钟情,也证明了夏树对于拓海来说是对的人,是有缘分的,是有环的。

等等!!!好像剧情不对!!!还有另一种情况,就是辣个男人出现了。

情况二:

假设有 3,2,0,4 四个节点,我们可以把它们想象成四个城市,然后有个男生叫拓海,有个女生叫夏树,拓海在3城市这个地方,而夏树在2城市这个地方,他们都有各自的生活轨迹,谁也不认识谁。

他们的生活总是向前的,只不过拓海生活节奏比较慢,每次只前进一次,夏树生活节奏快,每次前进两次。

第一天后变成

第二天后变成

此时已经发现,夏树还是选择了奔驰大叔,拓海和夏树还是终究还是错过了,这就说明拓海和夏树是错的人,是没有缘分,没有环的。

这说明,再快的86也追不上奔驰,兄弟们,把爷青结打在评论区上。

代码实现:

如何用代码复现我刚刚说的故事呢?

先准备两个指针,一个慢指针 slow,也就是拓海,一个快指针 fast,也就是夏树,判断 fast 指针是否还能继续走,如果可以,fast 走两步,slow 走一步。

可能存在一个疑惑,那就是为什么在有环的情况下,slow 走一步,fast 走两步,slow 和 fast 就一定会相遇???

这个问题就像两个人以不同的速度在操场跑圈,快的人一定会追上慢的人,画几个图说明一下大家就明白了

例如 slow 与 fast 的距离为n,那么每循环一次,slow 就会向前移动一步,此时slow 与 fast 的距离为 n + 1,但同样的 fast 会向前移动两步,此时slow 与 fast 的距离为 n + 1 - 2,也就是n - 1,那么也就是说,每循环一次,slow 与 fast 的距离就会减1,久而久之,fast 和 slow 就一定会相遇。

public class Solution {
    public boolean hasCycle(ListNode head) {
         if (head == null || head.next == null) return false;
	
		ListNode slow = head;   //准备一个慢指针
		ListNode fast = head.next;  //准备一个快指针
		while (fast != null && fast.next != null) { 
            //当快指针不为空,或者快指针的下一步不为空
/*
为什么这样设置循环条件fast.next != null???
因为快指针每次都走两次,如果快指针下一步为空,没有走的必要了,因为假设此时快指针继续走一步,
就会变成null,也就是说fast.next = null,由于每次快指针是走两步的,也就是意味着,快指针还得继续走多一步,那么会继续执行fast.next,但是由于上一个的fast.next已经变成null,这里调用next会出现空指针异常。
*/
			slow = slow.next;   //慢指针每次走一步
			fast = fast.next.next;  //快指针每次走两步
			
			if (slow == fast) return true;  //当慢快指针相遇,说明就是有环的
		}
		//代码执行到这里,说明快指针已经走完了,慢指针没有机会遇到快指针了
		return false;
    }
}

彩蛋:

实际上,快慢指针还可以用来寻找一条链表的中间节点,其中核心思想就是,链表的节点个数无非就是三种情况,要么0个,不然就是奇数个或者偶数个,我在代码中详细注释举例了三种情况。

题目链接:876. 链表的中间结点

解析+代码:

class Solution {
    /*
    双指针思想,慢指针slow一次走一步,快指针fast一次走两步,当快指针走完的时候,慢指针一定在链表的中间位置
    为什么呢?
    因为链表个数无论如何不是奇数就是偶数,要么就是0个,那么也就是说,只存在三种情况
    假如是奇数,1->2->3->4->5
    第一次,slow = 2,fast = 3
    第二次,slow = 3,fast = 5,此时fast.next == null 所以遍历结束,slow的确是中间节点
    假如是偶数,1->2->3->4->5->6
    第一次,slow = 2,fast = 3
    第二次,slow = 3,fast = 5
    第三次,slow = 4,fast = null,此时fast = null 所以遍历结束,slow的确是中间节点
    */
    public ListNode middleNode(ListNode head) {
        //准备快慢指针
        ListNode slow = head, fast = head;
        while(fast != null && fast.next != null){
            /*
            这里的fast.next != null和fast != null
            是为了防止下面的fast.next.next出现空指针异常
            */
            slow = slow.next;   //走一步
            fast = fast.next.next;  //走两步
        }
        return slow;
    }
}

都看到这里了,不考虑点个赞再走嘛!

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 21
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值