DeepSeek LeetCode 3892. 产生至少 K 个峰值的最少操作次数 JavaScript实现

📅 2026/8/13 23:54:40
DeepSeek    LeetCode 3892. 产生至少 K 个峰值的最少操作次数 JavaScript实现
这道题是力扣第 3892 题核心是动态规划 环形数组处理。我来给你一个清晰易懂的 JavaScript 实现。核心思路关键洞察1. 峰值不能相邻在环形数组中两个峰值之间至少隔一个元素所以最多 ⌊n/2⌋ 个峰值2. 操作成本要让 nums[i] 成为峰值只需把它增加到严格大于左右邻居的最大值3. 环形处理分两种情况讨论——选不选 nums[0]单点成本计算javascript// 让 nums[i] 成为峰值的成本const need Math.max(nums[i-1], nums[i1]) - nums[i] 1;const cost Math.max(0, need); // 只能增加不能减少完整代码实现 ACjavascript/*** param {number[]} nums* param {number} k* return {number}*/var minOperations function(nums, k) {const n nums.length;// 边界情况环形数组最多 floor(n/2) 个峰值if (k Math.floor(n / 2)) return -1;if (k 0) return 0;// 计算每个位置变成峰值的成本const cost new Array(n).fill(0);for (let i 0; i n; i) {const left nums[(i - 1 n) % n];const right nums[(i 1) % n];const need Math.max(left, right) - nums[i] 1;cost[i] Math.max(0, need);}// DP函数在数组arr中选择k个不相邻元素的最小成本function dp(arr, k) {const m arr.length;// dp[i][j] 表示前i个元素中选j个的最小成本const dp Array.from({ length: m 1 }, () new Array(k 1).fill(Infinity));// 初始化选0个成本为0for (let i 0; i m; i) {dp[i][0] 0;}for (let i 1; i m; i) {for (let j 1; j Math.min(k, Math.ceil(i / 2)); j) {// 情况1不选第i个元素dp[i][j] dp[i-1][j];// 情况2选第i个元素作为峰值// 需要跳过前一个元素所以从 i-2 转移if (i 2) {dp[i][j] Math.min(dp[i][j],dp[i-2][j-1] arr[i-1]);} else if (i 1 j 1) {// 只有一个元素时可以直接选dp[i][j] Math.min(dp[i][j], arr[0]);}}}return dp[m][k];}let ans Infinity;// 情况1不选 nums[0]范围 [1, n-1]const arr1 cost.slice(1);ans Math.min(ans, dp(arr1, k));// 情况2选 nums[0]范围 [2, n-2] 选 k-1 个if (k 1) {const arr2 cost.slice(2, n - 1);const result dp(arr2, k - 1);if (result ! Infinity) {ans Math.min(ans, result cost[0]);}}return ans Infinity ? -1 : ans;};优化版本滚动数组降维当 n 较大时可以用滚动数组将空间优化到 O(k)javascriptvar minOperations function(nums, k) {const n nums.length;if (k Math.floor(n / 2)) return -1;if (k 0) return 0;// 计算成本同上const cost new Array(n).fill(0);for (let i 0; i n; i) {const left nums[(i - 1 n) % n];const right nums[(i 1) % n];const need Math.max(left, right) - nums[i] 1;cost[i] Math.max(0, need);}// 滚动数组优化版DPfunction dp(arr, k) {const m arr.length;// prev 表示 dp[i-1]curr 表示 dp[i]let prev new Array(k 1).fill(Infinity);let curr new Array(k 1).fill(Infinity);prev[0] 0;curr[0] 0;for (let i 1; i m; i) {for (let j 1; j Math.min(k, Math.ceil(i / 2)); j) {// 不选当前元素curr[j] prev[j];// 选当前元素if (i 2) {curr[j] Math.min(curr[j],(i 2 ? prev : prev)[j-1] arr[i-1]);} else if (i 1 j 1) {curr[j] Math.min(curr[j], arr[0]);}}// 滚动[prev, curr] [curr, prev];}return prev[k];}// 分情况讨论同上let ans Infinity;const arr1 cost.slice(1);ans Math.min(ans, dp(arr1, k));if (k 1) {const arr2 cost.slice(2, n - 1);const result dp(arr2, k - 1);if (result ! Infinity) {ans Math.min(ans, result cost[0]);}}return ans Infinity ? -1 : ans;};测试用例javascript// 测试console.log(minOperations([2,1,2], 1)); // 输出: 1// 解释把 nums[1] 从1变成3数组变为 [2,3,2]峰值在索引1console.log(minOperations([4,5,3,6], 2)); // 输出: 0// 解释nums 本身就有两个峰值 5 和 6环形console.log(minOperations([3,7,3], 2)); // 输出: -1// 解释环形数组最多1个峰值k2 不可能console.log(minOperations([1,2,1,2,1], 2)); // 输出: 0// 解释已有的两个峰值足够console.log(minOperations([1,1,1,1], 2)); // 输出: 2// 解释可以选择索引1和3各加1变成 [1,2,1,2]复杂度分析· 时间复杂度O(n*k)n 是数组长度· 空间复杂度优化版 O(k)未优化版 O(n*k)关键注意事项1. 环形数组nums[0] 的左右邻居是 nums[n-1] 和 nums[1]2. 峰值不能相邻选择峰值时必须隔一个位置3. 只能增加成本计算用 max(0, need)不能通过减少邻居来实现4. 严格大于需要 1 操作如果还有疑问欢迎继续追问我可以针对某个具体细节再详细解释。