leetcode
leetcode 2251 ~ 2300
求出最多标记下标

求出最多标记下标

难度:

标签:

题目描述

You are given a 0-indexed integer array nums.

Initially, all of the indices are unmarked. You are allowed to make this operation any number of times:

  • Pick two different unmarked indices i and j such that 2 * nums[i] <= nums[j], then mark i and j.

Return the maximum possible number of marked indices in nums using the above operation any number of times.

 

Example 1:

Input: nums = [3,5,2,4]
Output: 2
Explanation: In the first operation: pick i = 2 and j = 1, the operation is allowed because 2 * nums[2] <= nums[1]. Then mark index 2 and 1.
It can be shown that there's no other valid operation so the answer is 2.

Example 2:

Input: nums = [9,2,5,4]
Output: 4
Explanation: In the first operation: pick i = 3 and j = 0, the operation is allowed because 2 * nums[3] <= nums[0]. Then mark index 3 and 0.
In the second operation: pick i = 1 and j = 2, the operation is allowed because 2 * nums[1] <= nums[2]. Then mark index 1 and 2.
Since there is no other operation, the answer is 4.

Example 3:

Input: nums = [7,6,8]
Output: 0
Explanation: There is no valid operation to do, so the answer is 0.

 

Constraints:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 109

 

代码结果

运行时间: 108 ms, 内存: 30.0 MB


// 思路:
// 1. 对数组进行排序。
// 2. 使用Java Stream生成一个包含所有有效对的流。
// 3. 对有效对的数量求和。

import java.util.Arrays;
import java.util.stream.IntStream;

public class Solution {
    public int maxMarkedIndices(int[] nums) {
        Arrays.sort(nums);  // 对数组进行排序
        boolean[] marked = new boolean[nums.length];  // 标记数组
        return (int) IntStream.range(0, nums.length)
            .filter(i -> !marked[i])
            .flatMap(i -> IntStream.range(i + 1, nums.length)
                .filter(j -> 2 * nums[i] <= nums[j] && !marked[j])
                .peek(j -> {
                    marked[i] = true;  // 标记i
                    marked[j] = true;  // 标记j
                }))
            .count() * 2;  // 返回结果
    }
}

解释

方法:

此题解采用排序加贪心的方法来解决问题。首先对数组 nums 进行排序,这样更容易找到满足 2 * nums[i] <= nums[j] 的 i 和 j。由于数组已排序,对于较小的元素,我们需要寻找后半部分中首个满足条件的较大元素。算法从数组的中点开始向后遍历,尝试找到与前半部分的元素 i 形成合法对的元素 j。如果找到了,i 指针向前移动,表示我们标记了一对下标。由于每次操作标记两个下标,所以返回的是成功匹配的对数乘以 2。

时间复杂度:

O(n log n)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在解题中选择排序后再进行双指针遍历,直接遍历不可以吗?
直接遍历整个数组来查找符合条件的下标对需要O(n^2)的时间复杂度,因为每个元素都需要与其他所有元素进行比较。通过先排序数组,可以利用数组的有序性,使用双指针方法来降低时间复杂度到O(n log n)(排序的时间复杂度),之后利用双指针线性遍历数组来找到符合条件的下标对,这样整体效率更高。
🦆
在进行排序操作后,原始数组的下标顺序被改变,这对算法找到的下标对有什么影响?
在本题的上下文中,目标是找到最多的满足条件的下标对的数量,而不是具体的下标位置。因此,改变原始数组的下标顺序对结果的数目没有影响。如果需要原始下标,可以在排序前存储每个元素的原始下标,然后在找到对应下标对后再映射回原数组的下标。
🦆
算法中为什么从数组的中点开始遍历,直接从头到尾遍历不可以吗?
从数组的中点开始遍历是基于问题条件 2 * nums[i] <= nums[j] 的特性。排序后的数组是递增的,因此较小的元素在数组的前半部分,较大的元素在后半部分。从中点开始向后遍历可以更快地找到满足条件的 j。如果从头开始遍历,对于每个 i,可能需要遍历多个 j 才能找到满足条件的第一个 j,这样会增加不必要的计算和时间复杂度。
🦆
对于数组的每一个元素i,算法如何保证每次都能找到满足条件的最优j?
算法通过排序保证数组是有序的,然后利用双指针策略,其中一个指针 i 从前半部分开始,另一个指针从中点开始向后寻找 j。由于数组已排序,一旦找到满足 2 * nums[i] <= nums[j] 的 j,就可以确定这是对于当前 i 来说最左边(也就是最优的)满足条件的 j。因此,算法每次都能为每个 i 找到最优的 j,从而保证结果的正确性。

相关问题