LeetCode 每日一题笔记 日期:2026.05.08 题目:3629. 素数跳跃最小次数

LeetCode 每日一题笔记

0. 前言

  • 日期:2026.05.08
  • 题目:3629. 素数跳跃最小次数
  • 难度:中等
  • 标签:数组 广度优先搜索 哈希表 数学(素数)

1. 题目理解

问题描述

给你一个整数数组 nums,你可以从下标 i 跳跃到下标 j,满足以下任一条件:

  1. j = i + 1j = i - 1(相邻下标);
  2. nums[i]nums[j] 存在共同的素数因子。

请你返回从数组起始下标 0 到末尾下标 n-1最小跳跃次数 ,若无法到达则返回 -1

示例

输入:nums = [2,3,4,9]

输出:2

解释:

最优路径:0 → 2 → 3,共2次跳跃。

  • 0到2:nums[0]=2 和 nums[2]=4 共享素因子2;
  • 2到3:nums[2]=4 和 nums[3]=9 无共享素因子,走相邻下标跳跃。

2. 解题思路

核心观察

  • 最小跳跃次数属于无权图最短路径问题,BFS是天然的最优解法;
  • 两种跳跃规则本质是图的边,相邻下标为基础边,共享素因子为批量连通边;
  • 需先预处理素数判断与质因数分解,建立素数到对应下标的映射,避免重复计算;
  • 处理过的素数可直接移除,避免重复入队,大幅优化BFS效率。

算法步骤

  1. 素数预处理:遍历数组,判断每个元素是否为素数,存入素数集合;
  2. 质因数分解:对数组每个元素分解素因子,建立「素数→对应下标列表」的哈希映射;
  3. BFS求最短路径
    • 初始化队列,从下标0开始,标记已访问节点;
    • 每层遍历对应一步跳跃,先处理相邻下标的合法跳跃,再处理同素因子的批量跳跃;
    • 处理完的素数从映射中移除,避免重复处理;
    • 到达数组末尾下标时,立即返回当前步数;
  4. 若队列遍历完成仍未到达终点,返回-1。

3. 代码实现

java 复制代码
package lc3629;

import java.util.*;


class Solution {
    public static boolean isPrime(int num) {
        if (num < 2) {
            return false;
        }
        if (num == 2) {
            return true;
        }
        if (num % 2 == 0) {
            return false;
        }
        for (int i = 3; i <= Math.sqrt(num); i += 2) {
            if (num % i == 0) {
                return false;
            }
        }
        return true;
    }


    public static Set<Integer> getPrimeFactors(int num) {
        Set<Integer> factors = new HashSet<>();
        if (num <= 1) return factors;
        if (num % 2 == 0) {
            factors.add(2);
            while (num % 2 == 0) num /= 2;
        }
        for (int i = 3; i <= Math.sqrt(num); i += 2) {
            if (num % i == 0) {
                factors.add(i);
                while (num % i == 0) num /= i;
            }
        }
        if (num > 1) factors.add(num);
        return factors;
    }

    public int bfs(int[] nums, HashMap<Integer, List<Integer>> map, Set<Integer> primeSet) {
        int n = nums.length;
        Queue<Integer> queue = new ArrayDeque<>(); // 优化3
        boolean[] visited = new boolean[n];
        queue.add(0);
        visited[0] = true;
        int step = 0;

        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                int curIdx = queue.poll();
                if (curIdx == n - 1) return step;

                if (curIdx - 1 >= 0 && !visited[curIdx - 1]) {
                    visited[curIdx - 1] = true;
                    queue.add(curIdx - 1);
                }
                if (curIdx + 1 < n && !visited[curIdx + 1]) {
                    visited[curIdx + 1] = true;
                    queue.add(curIdx + 1);
                }


                int curNum = nums[curIdx];
                if (primeSet.contains(curNum)) {
                    List<Integer> targetList = map.get(curNum);
                    if (targetList != null) {
                        for (int targetIdx : targetList) {
                            if (!visited[targetIdx] && targetIdx != curIdx) {
                                visited[targetIdx] = true;
                                queue.add(targetIdx);
                            }
                        }
                        map.remove(curNum);
                    }
                }
            }
            step++;
        }
        return -1;
    }

    public int minJumps(int[] nums) {
        int n = nums.length;
        if (n == 1) return 0;

        Set<Integer> primeSet = new HashSet<>();
        for (int num : nums) {
            if (isPrime(num)) {
                primeSet.add(num);
            }
        }


        HashMap<Integer, List<Integer>> map = new HashMap<>();
        for (int i = 0; i < n; i++) {
            Set<Integer> factors = getPrimeFactors(nums[i]);
            for (int p : factors) {
                if (primeSet.contains(p)) {
                    map.computeIfAbsent(p, k -> new ArrayList<>()).add(i);
                }
            }
        }


        return bfs(nums, map, primeSet);
    }
}

4. 代码优化说明

  1. 素数判断优化:提前排除偶数,仅遍历到数值平方根,减少无效循环次数;
  2. 质因数分解优化:先处理2的因子,再仅遍历奇数因子,降低循环次数;
  3. BFS队列优化:使用ArrayDeque替代LinkedList,提升入队、出队的执行效率;
  4. 去重优化:处理完一个素数的所有对应下标后,直接从哈希表移除该素数,避免重复入队和重复处理;
  5. 提前终止:BFS遍历中到达数组末尾时立即返回步数,无需遍历全图。

5. 复杂度分析

  • 时间复杂度 :O(n×max(nums))O(n \times \sqrt{max(nums)})O(n×max(nums) )

    • 素数判断与质因数分解的瓶颈为每个数值的平方根遍历,总复杂度为 O(n×max(nums))O(n \times \sqrt{max(nums)})O(n×max(nums) );
    • BFS遍历中每个下标最多入队一次,每个素数最多处理一次,额外开销为线性级 O(n)O(n)O(n)。
  • 空间复杂度 :O(n+p)O(n + p)O(n+p)

    • nnn 为数组长度,用于存储访问数组、队列、哈希映射;
    • ppp 为数组元素范围内的素数个数,用于存储素数集合与素数映射。

6. 总结

  • 本题核心是BFS求无权图最短路径 + 素数分解建图,将跳跃规则转化为图的连通边,用BFS天然解决最小步数问题;
  • 关键技巧:通过质因数分解建立素数到下标的映射,将共享素因子的跳跃转化为批量边处理,大幅减少建图开销;
  • 优化核心在于素数相关计算的效率提升,以及BFS过程中的去重处理,避免无效遍历;
  • 本题是图论搜索与数学素数知识结合的经典题型,BFS是解决此类最小步数问题的最优方案。
相关推荐
叼烟扛炮1 小时前
C++ 知识点08 类与对象
开发语言·c++·算法·类和对象
米粒12 小时前
力扣算法刷题 Day 63 Bellman_ford 算法
数据库·算法·leetcode
贺一航【Niki】8 小时前
【学习笔记】杂乱知识
笔记·学习
IT大白鼠8 小时前
AIGC性能的关键瓶颈:算力、数据、算法三者如何互相制约?
算法·aigc
白雪茫茫9 小时前
监督学习、半监督学习、无监督学习算法详解
python·学习·算法·ai
FengyunSky9 小时前
浅析 空间频率响应 SFR 计算
算法
つ安静与叛逆的小籹人9 小时前
小红书API:通过笔记ID获取笔记详情数据教程
笔记·python
树下水月9 小时前
PHP 一种改良版的雪花算法
算法·php·dreamweaver
一只数据集10 小时前
全尺寸人形机器人灵巧手力觉触觉数据集-2908条ROSbag数据覆盖14大应用场景深度解析
大数据·人工智能·算法·机器人