返回

解密下跳棋算法:LeetCode 1040 极富想象力的同向双指针模拟

闲谈

移动石子直到连续 II:同向双指针模拟算法的巧妙应用

简介

在编程的世界中,算法问题层出不穷,LeetCode 1040:移动石子直到连续 II 就是其中一道颇具挑战性的中等难度题目。这道题考验着解决问题的想象力和算法效率,以至于长期以来一直位居中等难度题目榜首。

题目

我们有一个由 0 和 1 组成的数组,目标是移动石子,使得数组中的 0 和 1 连续排列。每一次移动可以将一个石子移到相邻的位置,代价为 1。我们需要找到最小的移动代价,使得数组中的 0 和 1 连续排列。

解题思路:同向双指针模拟算法

面对这道看似复杂的题目,我们引入一种巧妙的算法——同向双指针模拟算法。这种算法使用两个指针同时遍历数组,当指针相遇时,表示数组中的 0 和 1 已经连续排列,从而计算出移动代价。

同向双指针模拟算法步骤

  1. 初始化两个指针,分别指向数组第一个元素和最后一个元素。
  2. 同时移动两个指针,直到指针相遇。
  3. 计算移动代价。
  4. 重复步骤 2 和步骤 3,直到数组中的 0 和 1 连续排列。

算法分析

同向双指针模拟算法之所以高效,是因为它只需遍历数组一次,时间复杂度为 O(n),其中 n 是数组的长度。与暴力枚举所有可能移动方案相比,这种算法大大降低了时间复杂度。

代码实现(Python)

def min_moves_to_make_continuous(nums):
    left, right = 0, len(nums) - 1
    moves = 0
    while left < right:
        if nums[left] == 0 and nums[right] == 1:
            nums[right] = 0
            right -= 1
        elif nums[left] == 1 and nums[right] == 0:
            nums[left] = 0
            left += 1
        else:
            left += 1
            right -= 1
    for num in nums:
        moves += num
    return moves

应用示例

考虑数组 nums = [0, 1, 0, 1, 1, 0, 1, 1, 0, 0],我们使用同向双指针模拟算法可以得到:

  • 初始化:left = 0, right = 9
  • 遍历数组:
    • nums[left] = 0, nums[right] = 0,移动右指针,right = 8
    • nums[left] = 0, nums[right] = 1,移动右指针,right = 7
    • ...
    • nums[left] = 1, nums[right] = 1,移动左指针,left = 1
    • ...
    • nums[left] = 0, nums[right] = 1,移动右指针,right = 3
  • 计算移动代价:3

结论

同向双指针模拟算法是一种强大的算法,可以高效解决移动石子直到连续 II 这样的问题。它的原理简单易懂,但应用起来却非常巧妙,大大降低了时间复杂度。

常见问题解答

  1. 为什么要使用两个指针?
    为了同时从数组的两端向中间移动,从而缩小需要移动的石子数量。

  2. 何时计算移动代价?
    当两个指针相遇时,此时数组中的 0 和 1 已经连续排列,可以计算出移动代价。

  3. 如果数组中没有 0 或 1,是否需要移动石子?
    不需要,因为数组中已经没有需要移动的石子。

  4. 同向双指针模拟算法的优点是什么?
    时间复杂度低,可以高效解决这类问题。

  5. 同向双指针模拟算法可以解决哪些其他问题?
    排序、求最大连续子数组和、求最长回文子串等问题。