leetcode
leetcode 2851 ~ 2900
游乐园的迷宫

游乐园的迷宫

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 874 ms, 内存: 16.6 MB


/*
题目思路:
1. 使用 Java Stream API 遍历 points 确定每次转向方向。
2. 使用 direction 字符串的每个字符确定下一个点。
3. 输出一个满足条件的路径。
*/
import java.util.*;
import java.util.stream.IntStream;

public class SalesmanPathStream {
    public List<Integer> findPath(int[][] points, String direction) {
        List<Integer> path = new ArrayList<>();
        path.add(0); // 从第一个点开始
        Set<Integer> visited = new HashSet<>();
        visited.add(0);
        int prev = 0;

        direction.chars().forEach(d -> {
            OptionalInt nextPoint = IntStream.range(1, points.length)
                    .filter(i -> !visited.contains(i))
                    .filter(i -> isValidTurn(points, prev, path.get(path.size() - 1), i, (char) d))
                    .findFirst();
            nextPoint.ifPresent(i -> {
                path.add(i);
                visited.add(i);
                prev = path.get(path.size() - 2);
            });
        });

        path.add(path.get(0)); // 回到起点
        return path;
    }

    private boolean isValidTurn(int[][] points, int prev, int current, int next, char direction) {
        int[] v1 = {points[current][0] - points[prev][0], points[current][1] - points[prev][1]};
        int[] v2 = {points[next][0] - points[current][0], points[next][1] - points[current][1]};
        int crossProduct = v1[0] * v2[1] - v1[1] * v2[0];
        return (direction == 'L' && crossProduct > 0) || (direction == 'R' && crossProduct < 0);
    }

    public static void main(String[] args) {
        SalesmanPathStream sp = new SalesmanPathStream();
        int[][] points = {{1, 3}, {2, 4}, {3, 3}, {2, 1}};
        String direction = "LR";
        System.out.println(sp.findPath(points, direction));
    }
}

解释

方法:

本题解采用的方法是基于模拟顺序搜索的策略,主要步骤包括:1) 找到最左侧的点作为起始点;2) 根据给定的转向指令序列依次选择下一个访问点。在选择过程中,如果当前转向指令是'L',则从未访问的点中选择相对于当前向量方向最右侧的点;如果指令是'R',则选择相对方向最左侧的点。这通过计算向量的叉积来判定。最后,将未访问的点添加到路径中作为终点。这种方法确保了每个点被恰好访问一次,并且满足转向的要求。

时间复杂度:

O(N^2)

空间复杂度:

O(N)

代码细节讲解

🦆
在确定最左侧的点作为起始点的基础上,如何保证任意起点和终点的选择也能满足题目要求?
在这种模拟顺序搜索中,选择最左侧的点作为起始点是为了确保一个固定的、可预测的起始位置,这有助于简化算法的设计和随后的路径规划。如果改变起点,算法需要适当调整以确保路径的合理性和符合转向要求。实际上,任意起点的选择都可以通过调整搜索策略(即如何选择下一个点)来实现,但这可能需要重新定义转向逻辑,以确保最终路径的有效性和连续性。终点的选择通常是所有未访问点中的最后一个,这确保了所有点都被访问一次,满足题目的完整性要求。
🦆
在寻找转向'L'时最右侧点和转向'R'时最左侧点的过程中,叉积的符号是如何准确反映点的相对位置的?
叉积是一个重要的几何工具,用于确定两个向量的相对方向。在二维空间中,向量A到向量B的叉积结果如果为正,则B在A的逆时针方向;如果为负,则B在A的顺时针方向。在算法中,通过计算从当前点到候选点的向量与从当前点到已选择的下一个点的向量的叉积,可以判断候选点是在当前转向方向的左侧还是右侧。这样,对于'L'的转向,我们选择叉积为负的最大值点(即最右侧),而对于'R'的转向,我们选择叉积为正的最大值点(即最左侧)。
🦆
叉积计算中,为什么向量的起点始终是上一个选择的点,这种设置对算法的结果有何影响?
在此题解中,始终以最近选择的点作为新的向量的起点是为了从当前位置出发,以此来确定下一步的最优选择点。这种方式能够确保路径的连续性和根据指定的转向规则(左或右)进行适当的转向。如果不以最近选择的点作为起点,路径可能会断裂或不符合转向规则,导致无法形成一条有效的、符合题目要求的访问路径。因此,这种设置对于保持算法在每一步都正确地考虑相对方向至关重要。

相关问题