青训营X豆包MarsCode 技术训练营第三课 | 豆包MarsCode AI 刷题

179 阅读3分钟

Java代码解析:解决石子移动问题的算法实现

问题描述

给定一个整数数组stones,数组中的每个元素代表一个石子的位置。这些石子排成一行,每个石子都可以移动到相邻的空位上。目标是通过最少的移动次数,使得所有石子都相邻(即任意两个石子之间的间隔为1)。数组中的空位可以认为是那些不在stones数组中的正整数。我们需要计算并返回达到这一目标所需的最少移动次数。

代码实现解析

下面是对提供的Java代码进行逐步解析,以理解其逻辑和计算过程。

  1. 初始条件处理

    java复制代码
    	if (stones.length == 1) {
    
    	    return 0;
    
    	}
    

    如果数组中只有一个石子,那么它已经满足条件,无需移动,直接返回0。

  2. 排序石子位置

    java复制代码
    	Arrays.sort(stones);
    

    为了简化计算,首先对石子位置进行排序。

  3. 计算最大可能移动次数

    java复制代码
    	int maxMoves = Math.max(stones[n - 1] - stones[1], stones[n - 2] - stones[0]) - (n - 2);
    

    这个计算基于这样一个观察:石子之间的最大间隔决定了可能的最大移动次数。我们考虑最左侧和最右侧的石子,以及次左侧和次右侧的石子,计算它们之间的间隔,然后减去(n - 2)(因为n个石子之间最多有(n - 1)个间隔,但要全部填满至少需要减少(n - 2)个间隔)。

  4. 计算最小移动次数

    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++;
    
    	    }
    
    	    // 如果窗口内有 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);
    
    	    }
    
    	}
    

    这部分代码使用了滑动窗口技巧来寻找最小的移动次数。

    • ij分别表示窗口的左右边界。
    • 窗口的大小由stones[j] - stones[i] + 1决定,并限制为最多n个位置。
    • alreadyInPlace计算当前窗口内已经有多少石子。
    • 如果窗口内有n - 1个石子且仅有一个空位(意味着可以通过移动一个石子来填满所有空位),则特殊处理,更新minMoves为2(因为最坏情况下需要两次移动)。
    • 否则,更新minMoves为当前窗口外需要移动的石子数量(即n - alreadyInPlace)。
  5. 返回结果

    java复制代码
    	return maxMoves;
    

    代码中的返回值是maxMoves,但这里存在一个明显的逻辑错误。根据问题描述,我们应该返回的是minMoves,而不是maxMoves。因此,正确的返回语句应该是:

    java复制代码
    	return minMoves;
    

修正后的代码

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 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++) {

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

	            }

	        }

	 

	        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

	    }

	}

通过这段代码,我们解决了石子移动问题,并计算出了达到目标所需的最少移动次数。