一次遍历获取数组最大值和第二大值(最小值和第二小值)
前言
最近做了两道题,有了一点想法,记录一下
问题
问题一:递增的三元子序列
给你一个整数数组 nums ,判断这个数组中是否存在长度为 3 的递增子序列。
如果存在这样的三元组下标 (i, j, k) 且满足 i < j < k ,使得 nums[i] < nums[j] < nums[k] ,返回 true ;否则,返回 false 。
示例 1:
输入:nums = [1,2,3,4,5]
输出:true
解释:任何 i < j < k 的三元组都满足题意
示例 2:输入:nums = [5,4,3,2,1]
输出:false
解释:不存在满足题意的三元组
示例 3:输入:nums = [2,1,5,0,4,6]
输出:true
解释:三元组 (3, 4, 5) 满足题意,因为 nums[3] == 0 < nums[4] == 4 < nums[5] == 6
方法一:双向遍历
创建两个长度为 n 的数组 leftMin 和 rightMax,对于 0≤i 得到数组 leftMin 和 rightMax 之后,遍历 1≤i 此方法需要三次遍历,并且需要O(n)的时间复杂度 方法二:贪心 a 始终记录最小元素,b 为某个子序列里第二大的数。 接下来不断更新 a,同时保持 b 尽可能的小。 如果下一个元素比 b 大,说明找到了三元组。 给你一个整数数组 nums ,其中总是存在 唯一的 一个最大整数 。 请你找出数组中的最大元素并检查它是否 至少是数组中每个其他数字的两倍 。如果是,则返回 最大元素的下标 ,否则返回 -1 。 示例 1: 输入:nums = [3,6,1,0] 输入:nums = [1,2,3,4] 输入:nums = [1] 除了遍历以外,很直观的一个想法,为什么我们要将最大值与所有元素进行比较呢?如果我们找到第二大的元素,将它的两倍的值与最大值进行比较,不就能证明最大值是否大于所有元素两倍的值吗? 正好上面那道题目的贪心就是记录数组中的最小值和次小值,我们换一下判断符号就可以了,于是,我写下如下代码: 其中,a_ind是为了记录最大值的下标,但是,这个方法在遇到某些测试用例的时候是通不过的。 由于求最大值和次大值 与 求最小值和次小值 的逻辑是一样的,我们用求最小值和次小值来测试一下 借助题目一的贪心策略代码,如下: 结果为: >>> (1, 2) 可以看出,在第三个测试用例中,最小值是1,但是次小值是6,结果是错的,原因在于,更新最小值的时候,没有把最小值更新给次小值,因此将核心代码加上那个一行赋值即可 上述题目二的正确解法也是如此,加上一行赋值即可,代码如下: 这个问题解决了,另一个问题来了,为什么对于题目一,贪心解法没有在更新最小值时,将最小值赋值给次小值,为怎么结果是对的呢? 原因很简单,因为题目找的是有没有符合要求的序列是否存在,而不是具体的值。 尽管下标不满足要求,但不会对结果的true/false判断造成影响,因为if的顺序,次小值b其实记录了一个额外的信息:自己前面有一个比自己小的元素。 例如,对于序列 [5, 6, 7, 1] , 一开始a=5,b=6,b可以等于6就已经代表了其前面出现过比它小的数组,当a更新为1时,不影响,这时候如果找到了比b大的数字,就代表找到了递增子序列。 1 class Solution {
2 public boolean increasingTriplet(int[] nums) {
3 int n = nums.length;
4 if (n < 3) {
5 return false;
6 }
7 int[] leftMin = new int[n];
8 leftMin[0] = nums[0];
9 for (int i = 1; i < n; i++) {
10 leftMin[i] = Math.min(leftMin[i - 1], nums[i]);
11 }
12 int[] rightMax = new int[n];
13 rightMax[n - 1] = nums[n - 1];
14 for (int i = n - 2; i >= 0; i--) {
15 rightMax[i] = Math.max(rightMax[i + 1], nums[i]);
16 }
17 for (int i = 1; i < n - 1; i++) {
18 if (nums[i] > leftMin[i - 1] && nums[i] < rightMax[i + 1]) {
19 return true;
20 }
21 }
22 return false;
23 }
24 }
1 class Solution:
2 def increasingTriplet(self, nums):
3 a = float("inf")
4 b = float("inf")
5
6 for i in nums:
7 if i <= a:
8 a = i
9 elif i <= b:
10 b = i
11 else:
12 return True
13
14 return False
问题二:至少是其他数字两倍的最大数
输出:1
解释:6 是最大的整数,对于数组中的其他整数,6 大于数组中其他元素的两倍。6 的下标是 1 ,所以返回 1 。
示例 2:
输出:-1
解释:4 没有超过 3 的两倍大,所以返回 -1 。
示例 3:
输出:0
解释:因为不存在其他数字,所以认为现有数字 1 至少是其他数字的两倍。 1 class Solution:
2 def dominantIndex(self, nums: List[int]) -> int:
3 if len(nums) == 1:
4 return -1
5
6 a = float("-inf")
7 a_ind = -1
8 b = float("-inf")
9
10
11 for i, val in enumerate(nums):
12 if val > a:
13 a = val
14 a_ind = i
15 elif val >b:
16 b = val
17
18
19 if a>=2*b:
20 return a_ind
21 return -1
验证求求最小值和次小值
class Solution:
def getNum(self, nums):
a = float("inf")
b = float("inf")
for i, val in enumerate(nums):
if val <= a:
a = val
elif val <= b:
b = val
return a, b
if __name__ == '__main__':
s = Solution()
nums = [1, 2, 7, 6]
print(s.getNum(nums))
nums = [5, 6, 6, 7]
print(s.getNum(nums))
nums = [5, 6, 1, 7]
print(s.getNum(nums))
(5, 6)
(1, 6) 1 class Solution:
2 def getNum(self, nums):
3 a = float("inf")
4 b = float("inf")
5
6 for i, val in enumerate(nums):
7 if val <= a:
8 b = a
9 a = val
10 elif val <= b:
11 b = val
12
13 return a, b
1 class Solution:
2 def dominantIndex(self, nums: List[int]) -> int:
3 a = float("-inf")
4 a_ind = -1
5 b = float("-inf")
6
7 for i, val in enumerate(nums):
8 if val > a:
9 b = a
10 a = val
11 a_ind = i
12 elif val > b:
13 b = val
14
15 if a >= 2 * b:
16 return a_ind
17 return -1