leetcode
leetcode 2451 ~ 2500
删除数对后的最小数组长度

删除数对后的最小数组长度

难度:

标签:

题目描述

You are given a 0-indexed sorted array of integers nums.

You can perform the following operation any number of times:

  • Choose two indices, i and j, where i < j, such that nums[i] < nums[j].
  • Then, remove the elements at indices i and j from nums. The remaining elements retain their original order, and the array is re-indexed.

Return an integer that denotes the minimum length of nums after performing the operation any number of times (including zero).

Note that nums is sorted in non-decreasing order.

 

Example 1:

Input: nums = [1,3,4,9]
Output: 0
Explanation: Initially, nums = [1, 3, 4, 9].
In the first operation, we can choose index 0 and 1 because nums[0] < nums[1] <=> 1 < 3.
Remove indices 0 and 1, and nums becomes [4, 9].
For the next operation, we can choose index 0 and 1 because nums[0] < nums[1] <=> 4 < 9.
Remove indices 0 and 1, and nums becomes an empty array [].
Hence, the minimum length achievable is 0.

Example 2:

Input: nums = [2,3,6,9]
Output: 0
Explanation: Initially, nums = [2, 3, 6, 9]. 
In the first operation, we can choose index 0 and 2 because nums[0] < nums[2] <=> 2 < 6. 
Remove indices 0 and 2, and nums becomes [3, 9]. 
For the next operation, we can choose index 0 and 1 because nums[0] < nums[1] <=> 3 < 9. 
Remove indices 0 and 1, and nums becomes an empty array []. 
Hence, the minimum length achievable is 0.

Example 3:

Input: nums = [1,1,2]
Output: 1
Explanation: Initially, nums = [1, 1, 2].
In an operation, we can choose index 0 and 2 because nums[0] < nums[2] <=> 1 < 2. 
Remove indices 0 and 2, and nums becomes [1]. 
It is no longer possible to perform an operation on the array. 
Hence, the minimum achievable length is 1. 

 

Constraints:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 109
  • nums is sorted in non-decreasing order.

代码结果

运行时间: 40 ms, 内存: 26.3 MB


/*
 * 思路:
 * 与Java版本相同,通过流操作简化代码。
 * 仍然是寻找最长的不下降子序列,然后剩余的元素就是我们需要移除的。
 */

import java.util.*;
import java.util.stream.IntStream;

public class Solution {
    public int minLengthAfterRemovals(int[] nums) {
        int n = nums.length;
        int[] dp = new int[n];
        Arrays.fill(dp, 1);
        IntStream.range(1, n).forEach(i -> 
            IntStream.range(0, i).forEach(j -> {
                if (nums[i] >= nums[j]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            })
        );
        int maxLength = Arrays.stream(dp).max().orElse(1);
        return n - maxLength;
    }
}

解释

方法:

此题解的核心思路在于找出数组中的中间值,并确定这个值的最大出现次数。由于数组是非递减的,这个中间值可以代表数组的中值或众数。题解利用这个中间值的最大出现次数,尝试将数组分成尽可能多的配对,每对满足较小数小于较大数的条件,从而使数组长度最小化。由于每次操作都会移除两个数字,所以解集中考虑了两种情况:1) 若最大出现次数乘以二小于数组长度,返回数组长度取模二的结果;2) 若不小于,则返回0或1,具体取决于数组长度是奇数还是偶数。这种方法有效地利用了数组的非递减特性,通过统计中间值的出现频率来最大化配对的可能。

时间复杂度:

O(log n)

空间复杂度:

O(1)

代码细节讲解

🦆
在算法中,为什么选择数组的中间值作为关键元素来对数组进行操作?
在非递减的数组中,中间值可以代表数组的中值或众数,这意味着它是数组中出现频率可能最高的一个元素。通过选择中间值作为关键元素,可以最大化可能的配对数,因为配对需要一个较小和一个较大的数,中间值的高频率增加了找到可配对元素的可能性。此外,选择中间值简化了实现,因为可以直接通过索引快速访问到它,并利用二分查找快速统计其出现次数。
🦆
算法如何确保通过删除操作得到的数组长度是最小的?即如何证明这种方法能达到题目要求的最小长度?
该算法通过最大化配对的数目来最小化剩余数组的长度。选择中间值并统计其出现次数因其可能的高频率,使得配对的概率最大化。理论上,若中间值的出现次数较高,则可以与其他元素配对形成删除对,最大化删除操作的数量。当配对数最大化时,剩余未配对的元素数目自然最小。如果中间值的出现次数乘以二小于数组长度,意味着不能全部配对,剩余的元素数为数组长度的模二结果;如果不小于,则可能将所有元素配对完毕,剩余长度为0或1,取决于数组原始长度的奇偶性。
🦆
对于具有相同中间值的多个元素,算法是如何决定删除哪些元素对的?是否考虑了所有可能的元素对组合?
算法并没有具体指定删除哪些具体的元素对,而是依赖于中间值的出现次数来最大化可能的配对数。因为题解的核心在于统计中间值的出现频次,并尝试将其与其他元素配对,所以它不需要考虑具体的配对组合。算法的目标是通过最大化使用中间值的出现次数来减少数组长度,具体哪些元素被配对并未具体指明,这是一个理论上的最优化处理,实际上的实现可能需要具体的配对策略。
🦆
算法中提到的`max_cnt * 2 - n`和`n % 2`,这两个操作的逻辑依据是什么?如何通过这两个计算确定最终的数组长度?
`max_cnt * 2 - n`计算的是使用中间值最大化配对后可能的剩余元素数量。如果`max_cnt * 2`小于`n`,这意味着即便所有中间值都能找到配对,仍有元素无法被配对,剩余元素数即为`n - max_cnt * 2`;如果`max_cnt * 2`大于等于`n`,理论上可以完全配对或最多剩下一个元素。`n % 2`用于处理数组长度为奇数的情况,因为奇数长度的数组无法完全配对,至少会剩下一个元素。这两个计算结合起来,允许算法在不同情况下给出最小的剩余数组长度。

相关问题