23石子移动问题-青训营刷题

问题描述

小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

代码说明

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);
    }
}

 代码解释

这段代码实现了一个关于石子移动的问题,目标是计算将一组石子移动到连续位置所需的最小和最大移动次数。

代码功能分析

1. 输入和排序

java复制

if (stones.length == 1) {
    return 0;
}
Arrays.sort(stones);
int n = stones.length;
  • 如果石子数组的长度为1,直接返回0,因为不需要移动。

  • 对石子数组进行排序,以便后续计算。

2. 计算最大移动次数

java复制

int maxMoves = Math.max(stones[n - 1] - stones[1], stones[n - 2] - stones[0]) - (n - 2);
  • 这一行试图计算最大移动次数。逻辑是基于数组排序后的结果:

    • stones[n - 1] - stones[1]:计算从第二个石子到最后一个石子之间的最大跨度。

    • stones[n - 2] - stones[0]:计算从第一个石子到倒数第二个石子之间的最大跨度。

    • 减去(n - 2)是为了考虑中间的石子数量,假设每个石子都需要移动到一个连续的位置。

3. 计算最小移动次数

java复制

int minMoves = Integer.MAX_VALUE;
int j = 0;
for (int i = 0; i < n; i++) {
    while (j < n && stones[j] - stones[i] + 1 <= n) {
        j++;
    }
    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);
    }
}
  • 使用滑动窗口技术来计算最小移动次数。

  • ij分别表示窗口的起始和结束位置。

  • while循环尝试扩展窗口,直到窗口内的石子数量超过n

  • alreadyInPlace表示窗口内已经就位的石子数量。

  • 如果窗口内有n - 1个石子,并且这些石子的跨度为n - 1,则需要特殊处理,将minMoves设置为2。

  • 否则,计算需要移动的石子数量为n - alreadyInPlace

4. 返回结果

java复制

return maxMoves;
  • 代码最终返回maxMoves,但根据问题描述,似乎应该返回minMoves

问题和改进建议

1. 逻辑问题
  • maxMoves的计算逻辑可能不正确。它试图通过计算最大跨度来估计最大移动次数,但这可能不是最优解。

  • minMoves的计算逻辑中,特殊处理的条件stones[j - 1] - stones[i] + 1 == n - 1可能不完全正确。

2. 返回值问题
  • 根据问题描述,代码应该返回最小移动次数minMoves,而不是maxMoves

3. 测试用例问题
  • 测试用例的输出使用了==,这在Java中是不正确的。应该使用System.out.println直接输出结果,而不是比较返回值。

改进后的代码

以下是改进后的代码,确保返回最小移动次数,并修正了一些逻辑问题:

java复制

import java.util.Arrays;

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

        // 计算最小移动次数,使用滑动窗口
        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 minMoves;
    }

    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
    }
}

改进点

  1. 返回值修正:返回minMoves而不是maxMoves

  2. 测试用例修正:直接输出结果,而不是比较返回值。

  3. 逻辑优化:确保滑动窗口逻辑正确处理特殊情况。

通过这些改进,代码应该能够正确计算将石子移动到连续位置所需的最小移动次数。

你可能感兴趣的:(算法,java,数据结构)