1966. 未排序数组中的可被二分搜索的数 🔒
题目描述
有一个 类似 二分搜索的方法。 这个方法有两个入参: sequence
是一个整数数组, target
是一个整数。 这个方法可以判断 target
是否存在 sequence
中。
该方法的伪代码如下:
func(sequence, target) while sequence is not empty randomly choose an element from sequence as the pivot if pivot = target, return true else if pivot < target, remove pivot and all elements to its left from the sequence else, remove pivot and all elements to its right from the sequence end while return false
当 sequence
是排好序时, 这个方法对 所有 值都可正常判断。如果 sequence
不是排好序的, 该方法并不是对所有值都可正常判断, 但对一些 值仍可正常判断。
给定一个仅包含不同数字的数组 nums
表示 sequence
, nums是否排序未知,对于 所有可能的选择, 返回通过这个方法保证能找到的值的数量。
示例 1:
输入: nums = [7] 输出: 1 解释: 7 保证能被找到. 因为数组中只有一个数字, 7 一定会被选中. 因为选中的值等于target, 这个方法会返回 true.
示例 2:
输入: nums = [-1,5,2] 输出: 1 解释: 只有 -1 保证能被找到. 如果 -1 被选中, 这个方法就会返回 true. 如果 5 被选中, 5 和 2 会被移除。 在下一次循环时, 这个序列只有一个元素: -1 ,这个方法就会返回 true. 如果 2 被选中, 2 将会被移除。 在下次循环时, 这个序列里将会有 -1 和 5. 无论哪个数字被选中, 这个方法都会找到 -1 且返回 true. 5 不能保证被找到。 如果 2 被选中, -1, 5 和 2 将会被移除。 这个序列将会被清空且这个方法会返回 false。 2 不能保证被找到. 如果 5 被选中, 5 和 2 将会被移除。在下次循环时, 这个序列只会有一个元素: -1 且这个方法会返回 false。 因为只有-1 是保证能被找到的, 你应该返回 1.
提示:
1 <= nums.length <= 105
-105 <= nums[i] <= 105
nums
中所有值都 不同.
提升: 如果 nums
存在 重复的值, 你会如何修改你的算法吗?
解法
方法一:维护前缀最大值和后缀最小值
我们注意到,对于数组中的每个元素,如果它是可被二分搜索的,那么需要满足两个条件:
- 这个元素大于它的左边所有元素,否则,如果左边存在比当前元素大的元素,那么就会被移除,导致无法找到当前元素;
- 这个元素小于它的右边所有元素,否则,如果右边存在比当前元素小的元素,那么就会被移除,导致无法找到当前元素。
我们创建一个数组 $ok$,其中 $ok[i]$ 表示 $nums[i]$ 是否是可被二分搜索的。初始时 $ok[i]$ 都为 $1$。
我们先从左到右遍历数组,维护前缀最大值 $mx$,如果当前元素 $x$ 比 $mx$ 小,那么 $x$ 就不是可被二分搜索的,我们将 $ok[i]$ 置为 $0$,否则,我们将 $mx$ 更新为 $x$。
然后我们从右到左遍历数组,维护后缀最小值 $mi$,如果当前元素 $x$ 比 $mi$ 大,那么 $x$ 就不是可被二分搜索的,我们将 $ok[i]$ 置为 $0$,否则,我们将 $mi$ 更新为 $x$。
最后我们统计 $ok$ 中的 $1$ 的个数,即为可被二分搜索的元素的个数。
时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $nums$ 的长度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|