leetcode
leetcode 2351 ~ 2400
重新放置石块

重新放置石块

难度:

标签:

题目描述

You are given a 0-indexed integer array nums representing the initial positions of some marbles. You are also given two 0-indexed integer arrays moveFrom and moveTo of equal length.

Throughout moveFrom.length steps, you will change the positions of the marbles. On the ith step, you will move all marbles at position moveFrom[i] to position moveTo[i].

After completing all the steps, return the sorted list of occupied positions.

Notes:

  • We call a position occupied if there is at least one marble in that position.
  • There may be multiple marbles in a single position.

 

Example 1:

Input: nums = [1,6,7,8], moveFrom = [1,7,2], moveTo = [2,9,5]
Output: [5,6,8,9]
Explanation: Initially, the marbles are at positions 1,6,7,8.
At the i = 0th step, we move the marbles at position 1 to position 2. Then, positions 2,6,7,8 are occupied.
At the i = 1st step, we move the marbles at position 7 to position 9. Then, positions 2,6,8,9 are occupied.
At the i = 2nd step, we move the marbles at position 2 to position 5. Then, positions 5,6,8,9 are occupied.
At the end, the final positions containing at least one marbles are [5,6,8,9].

Example 2:

Input: nums = [1,1,3,3], moveFrom = [1,3], moveTo = [2,2]
Output: [2]
Explanation: Initially, the marbles are at positions [1,1,3,3].
At the i = 0th step, we move all the marbles at position 1 to position 2. Then, the marbles are at positions [2,2,3,3].
At the i = 1st step, we move all the marbles at position 3 to position 2. Then, the marbles are at positions [2,2,2,2].
Since 2 is the only occupied position, we return [2].

 

Constraints:

  • 1 <= nums.length <= 105
  • 1 <= moveFrom.length <= 105
  • moveFrom.length == moveTo.length
  • 1 <= nums[i], moveFrom[i], moveTo[i] <= 109
  • The test cases are generated such that there is at least a marble in moveFrom[i] at the moment we want to apply the ith move.

代码结果

运行时间: 77 ms, 内存: 34.6 MB


/*
 * 思路:
 * 1. 使用Java Stream API来处理石块的位置变换。
 * 2. 用Map记录石块的初始位置。
 * 3. 使用IntStream处理moveFrom和moveTo数组,更新石块位置。
 * 4. 收集有石块的位置,按升序返回结果。
 */
import java.util.*;
import java.util.stream.*;

public class Solution {
    public List<Integer> relocateStones(int[] nums, int[] moveFrom, int[] moveTo) {
        Map<Integer, Integer> stoneMap = Arrays.stream(nums)
                                                .boxed()
                                                .collect(Collectors.toMap(k -> k, v -> 1, Integer::sum));
        IntStream.range(0, moveFrom.length).forEach(i -> {
            int from = moveFrom[i];
            int to = moveTo[i];
            if (stoneMap.containsKey(from)) {
                stoneMap.put(to, stoneMap.getOrDefault(to, 0) + stoneMap.get(from));
                stoneMap.remove(from);
            }
        });
        return stoneMap.keySet().stream().sorted().collect(Collectors.toList());
    }
}

解释

方法:

这个解决方案使用了一个集合(Set)来跟踪所有石块的位置。初始时,将 nums 数组中所有的位置加入到集合中,代表石块的初始位置。然后,通过遍历 moveFrom 和 moveTo 数组,对于每一对操作(从 moveFrom[i] 移动到 moveTo[i]),先从集合中移除 moveFrom[i](因为石块从此位置移走),然后将 moveTo[i] 加入到集合中(石块移动到新位置)。因为集合自动处理重复的元素,所以即使多次移动到同一个位置也不会有问题。最后,将集合转换为有序数组后返回,这样可以得到所有有石块的位置的升序列表。

时间复杂度:

O(n + m + k log k)

空间复杂度:

O(n + m)

代码细节讲解

🦆
在算法中,为什么在移动石块后从集合中删除moveFrom[i]位置,即使可能还有其他石块留在那里?
在这个算法中,我们假设每个位置最初只有一个石块,且每次移动操作只涉及一个石块。因此,当从moveFrom[i]位置移动石块时,我们认为该位置变为空,所以需要从集合中移除该位置。如果实际情况中某个位置可以有多个石块,那么这种假设就不成立,需要修改算法以记录每个位置的石块数量。
🦆
如果moveFrom数组中的某个位置在nums中不存在,该怎么处理?这种情况会影响最终的输出结果吗?
如果moveFrom数组中的某个位置原本就不存在于nums中,那么尝试从集合中移除该位置将不会有任何效果,因为集合中本来就没有这个元素。这种情况不会影响最终输出结果,因为最终输出的是集合中仍然存在的元素(即石块的最终位置)。
🦆
算法中提到的‘集合自动处理重复元素’是如何确保在多次移动到同一个位置时不会出现问题的?具体是如何操作的?
集合是一种数据结构,可以自动处理重复的元素,即它不会存储重复的值。当尝试向集合中添加一个已经存在的元素时,集合将不会再次添加该元素,保持元素的唯一性。因此,即使算法中多次将石块移动到同一个位置,集合中该位置只会被记录一次,避免了重复记录的问题。
🦆
将集合转换成有序数组的操作,为什么选择了排序而不是其他可能更高效的方法?
集合是一个无序的数据结构,如果需要按特定顺序(例如升序)查看或返回集合中的元素,必须对其进行排序。虽然排序操作有一定的计算成本,但由于集合的无序性质,排序是实现元素有序输出的直接和简单方法。在没有特定要求进行高效排序或数据量非常大的情况下,直接使用排序是一个有效且实用的选择。

相关问题