本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
今天做完leetcode的每日一题,顺带学习了一下贪心算法的分而治之的思想。
题目描述
今天的题目为leetcode上的 334. 递增的三元子序列 难度为 中等
-
给你一个整数数组 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
提示:
- 1 <= nums.length <= 5 * 105
- -231 <= nums[i] <= 231 - 1
- 进阶:你能实现时间复杂度为 O(n) ,空间复杂度为 O(1) 的解决方案吗?
贪心算法
贪心算法的根本思想就是一个问题如果暂时找不到它的最优解,可以考虑把它拆成几个小的问题,分别求出每个小问题的最优解,然后将他们叠加起来,当作整个问题的“最优解”。
贪心算法的三个步骤
-
明确什么是我们需要的最优解
-
分析子问题,并且明确子问题的最优解
-
分别求出子问题的最优解然后叠加出全局最优解
贪心算法的示例
贪心算法有一个经典的例子,就是背包问题:
有一个背包,最多能承载150斤的重量,现在有7个物品,重量分别为[35, 30, 60, 50, 40, 10, 25],它们的价值分别为[10, 40, 30, 50, 35, 40, 30],应该如何选择才能使得我们的背包背走最多价值的物品?
- 首先明确我们需要的最优解--带走价值最多的物品
- 局部最优解就有多种不同的选择,可以选择价值最高的,或者重量最轻的,又或者性价比最高的
- 根据选择的不同选取不同的物品
使用情况
- 全局最优解不好取得
- 全局最优解的数学模型难以建立
- 没必要取得最优解,可以接受接近最优的比较优的解答
看完了贪心算法的简单介绍,接下来回到题目中去运用
题解
分析
题目要求要三个一组的递增子序列,我们会发现三个一组并不好求得,但是我们可以先定好两个大小和下标都满足递增的子序列,然后遍历去查找第三个满足条件的值。
这就是运用了贪心算法的思想,三个子序列不好找,那就拆分问题,优先去找到满足条件的两个子序列,再去查找第三个。
所以我们维护两个变量first和second分别代表递增三元子序列的第一个和第二个数,并且一直满足first<second。接着对数组nums进行遍历操作,去查找满足条件的第三个数。
在初始化的时候,first = nums[0], second = +∞, 在遍历的过程中每一次遍历进行如下查找:
- 如果num[i] > second, 那就找到了我们需要的第三个数,返回true
- 否则的话,就判断num[i] > first, 那么就将second的值更新
- 再不然,就说明num[i]比现在的两个数都要小,将num[i]赋值给first 遍历完成之后都没有找到第三个数,那就说明数组中不存在这个情况,返回false
可以会奇怪,要是在second的位置后面出现了小于second的值,那么first的下标不就会变得比second大了吗,这并没有关系,因为我们知道在这之前存在一个数是小于second的,所以哪怕之后立马出现了第三个数,那么还是会存在这样一个三元子序列满足条件,也会返回true
代码
/**
* @param {number[]} nums
* @return {boolean}
*/
var increasingTriplet = function(nums) {
if (nums.length < 3) {
return false;
}
let first = nums[0], second = Number.MAX_VALUE;
for (let i = 1; i < nums.length; i++) {
if (nums[i] > second) {
return true;
} else if (nums[i] > first) {
second = nums[i];
} else {
first = nums[i];
}
}
return false;
};