6

PHP 快慢指针的进阶题

 3 years ago
source link: https://hxd.life/2020/10/10/PHP-%E5%BF%AB%E6%85%A2%E6%8C%87%E9%92%88%E7%9A%84%E8%BF%9B%E9%98%B6%E9%A2%98/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

PHP 快慢指针的进阶题

PHP 快慢指针的进阶题

author: he xiaodong date: 2020-10-10
环形链表 II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

说明:不允许修改给定的链表。

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/linked-list-cycle-ii

解题思路 1

遍历链表,同时将每次的结果放到 map 中,首次遇到重复元素,则直接返回这个元素就行,代码和上一篇文章的类似。

解题思路 2

快慢指针求解:我们定义两个指针,一快一满。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针在位置 head,而快指针在位置 head.next。这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。否则快指针将到达链表尾部,该链表不为环形链表。

确定有环形之后,就是推理找到入环点: 我们使用两个指针,fastslow。它们起始都位于链表的头部。随后,slow 指针每次向后移动一个位置,而 fast 指针向后移动两个位置。如果链表中存在环,则 fast 指针最终将再次与 slow 指针在环中相遇。

如下图所示,设链表中环外部分的长度为 aa。slow 指针进入环后,又走了 bb 的距离与 fast 相遇。此时,fast 指针已经走完了环的 nn 圈,因此它走过的总距离为 a + n(b + c) + b = a + (n + 1)b + nc

示例图

根据题意,任意时刻,fast 指针走过的距离都为 slow 指针的 22 倍。因此,我们有

a + (n + 1)b + nc = 2(a+b) ⟹ a = c + (n − 1)(b + c)

有了 a = c + (n - 1)(b + c) 的等量关系,我们会发现:从相遇点到入环点的距离加上 n-1 圈的环长,恰好等于从链表头部到入环点的距离。

因此,当发现 slowfast 相遇时,我们再额外使用一个指针 ptr。起始,它指向链表头部;随后,它和 slow 每次向后移动一个位置。最终,它们会在入环点相遇。

作者:LeetCode-Solution 链接:https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/huan-xing-lian-biao-ii-by-leetcode-solution/ 来源:力扣(LeetCode)

/**
 * Definition for a singly-linked list.
 * class ListNode {
 *     public $val = 0;
 *     public $next = null;
 *     function __construct($val) { $this->val = $val; }
 * }
 */

class Solution {
    /**
     * @param ListNode $head
     * @return ListNode
     */
    function detectCycle($head) {
        if($head == null || $head->next == null) return null;
        $fast = $head;
        $slow = $head;

        // 在相遇点跳出循环
        
        while ($fast != null && $fast->next != null) {
            $fast = $fast->next->next;
            $slow = $slow->next;
            if ($fast === $slow) {
                break;
            }
        }
        
        // 声明 ptr 指针,步长为 1 的向前走

        $ptr = $head;
        while ($ptr !== $slow) {
            $ptr = $ptr->next;
            $slow = $slow->next;
        }
        // ptr 即为入口

        return $ptr;
    }
}

类似的另外一题是:

寻找重复数

给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。

示例 1:

输入: [1,3,4,2,2]
输出: 2

示例 2:

输入: [3,1,3,4,2]
输出: 3

不能更改原数组(假设数组是只读的)。 只能使用额外的 O(1) 的空间。 时间复杂度小于 O(n2) 。 数组中只有一个重复的数字,但它可能不止重复出现一次。

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/find-the-duplicate-number

和上面的快慢指针一样的,只是借助索引将数组想象成链表就可以。

class Solution {

    /**
     * @param Integer[] $nums
     * @return Integer
     */
    function findDuplicate($nums) {
        $slow = 0;
        $fast = 0;
        $slow = $nums[$slow];
        $fast = $nums[$nums[$fast]];
        while($slow != $fast){
            $slow = $nums[$slow];
            $fast = $nums[$nums[$fast]];
        }
        
        $pre1 = 0;
        $pre2 = $slow;
        while($pre1 != $pre2){
            $pre1 = $nums[$pre1];
            $pre2 = $nums[$pre2];
        }
        return $pre1;
    }
}

参考链接:

  1. 环形链表 Ⅱ 官方题解

最后恰饭 阿里云全系列产品/短信包特惠购买 中小企业上云最佳选择 阿里云内部优惠券



About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK