青训营—石子移动问题

365 阅读3分钟

题目描述

小S正在玩一个关于石子的游戏,给定了一些石子,它们位于一维数轴的不同位置,位置用数组 stones 表示。如果某个石子处于最小或最大的一个位置,我们称其为端点石子

在每个回合,小S可以将一颗端点石子移动到一个未占用的位置,使其不再是端点石子。游戏继续,直到石子的位置变得连续,无法再进行任何移动操作。

你需要帮助小S找到可以移动的最大次数。

测试样例

样例1

输入:stones = [7, 4, 9]
输出:2

样例2

输入:stones = [6, 5, 4, 3, 10]
输出:3

样例3

输入:stones = [1, 2, 3, 4, 5]
输出:0

思路

  1. 理解端点石子:端点石子是指在一维数轴上位置最小或最大的石子。可以通过移动这些端点石子来尝试使得石子的位置变得连续。
  2. 移动次数的计算:
  • 首先,需要对石子的位置进行排序,以便于可以轻松找到最小和最大位置。
  • 然后,可以计算出在当前状态下,石子之间的空位数量。需要确保石子之间的间隔是连续的,即没有空位。
  1. 最大移动次数的计算:
  • 最大移动次数可以通过计算最远的两个端点之间的距离,并减去已经占用的石子数量来得到。具体来说,可以计算出:
  • maxMoves = max(stones[n - 1] - stones[1], stones[n - 2] - stones[0]) - (n - 2)
  • 这里,stones[n - 1]stones[1] 是排序后的最大和次小位置,stones[n - 2]stones[0] 是次大和最小位置。
  1. 最小移动次数的计算:使用滑动窗口的方法来计算最小移动次数。通过维护一个窗口,可以找到在某个范围内已经存在的石子数量,并计算出需要移动的石子数量。
  2. 返回结果:最后,返回计算出的最大移动次数。

Java代码

import java.util.Arrays;

public class Main {
    public static int solution(int[] stones) {
        // write code here
        if (stones.length == 1) {
            return 0;
        }
        Arrays.sort(stones);
        int n = stones.length;

        // 计算最大移动次数
        int maxMoves = Math.max(stones[n - 1] - stones[1], stones[n - 2] - stones[0]) - (n - 2);

        // 计算最小移动次数,使用滑动窗口
        int minMoves = Integer.MAX_VALUE;
        int j = 0;
        for (int i = 0; i < n; i++) {
            // 确保窗口内最多有 n 个石子
            while (j < n && stones[j] - stones[i] + 1 <= n) {
                j++;
            }
            // 如果窗口内有 n - 1 个石子,且空位为 1,则需要特殊处理
            int alreadyInPlace = j - i;
            if (alreadyInPlace == n - 1 && stones[j - 1] - stones[i] + 1 == n - 1) {
                minMoves = Math.min(minMoves, 2);
            } else {
                minMoves = Math.min(minMoves, n - alreadyInPlace);
            }
        }

        return maxMoves;
    }

    public static void main(String[] args) {
        System.out.println(solution(new int[]{7, 4, 9}) == 2);
        System.out.println(solution(new int[]{6, 5, 4, 3, 10}) == 3);
        System.out.println(solution(new int[]{1, 2, 3, 4, 5}) == 0);
    }
}

复杂度分析

时间复杂度:排序的时间复杂度为 (O(nlogn))。计算最大移动次数和最小移动次数的过程是线性的,即(O(n))。因此,总的时间复杂度为 (O(nlogn))。

空间复杂度:主要使用了排序所需的额外空间,空间复杂度为 (O(1))(如果不考虑排序的空间复杂度)。 - 总的空间复杂度为 (O(1))(不考虑输入数组的空间)。