35. 搜索插入位置 (简单)
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
就是普通的二分搜索,目标值不存在时,返回的是left ,因为结束条件是left = right + 1,结束的前一步分情况:left + 1 = right 或者 left = right;然后在具体每种if else情况下,left的下标都是一下特点。
392. 判断子序列 (简单)
双指针:
var isSubsequence = function(s, t) {
let i = j = 0, len = s.length;
for (; i<t.length && j<len; i++) {
if(s[j]==t[i]) j++;
}
return j==len
};
进阶二分查找:
如果给你一系列字符串s1,s2,…和字符串t。上述解法处理每个s时间复杂度仍然是 O(N),而如果巧妙运用二分查找,可以将时间复杂度降低,大约是 O(MlogN),M 为 s 的长度。
一系列字符串要快的话,内涵的意思就是提前准备一个字典,
主要是对t进行预处理,用一个字典index将每个字符出现的索引位置按顺序存储下来.
比如对于这个情况,匹配了 “ab”,应该匹配 “c” 了:
按照之前的解法,我们需要j线性前进扫描字符 “c”。但现在借助index中记录的信息,可以二分搜索index[c]中比 j 大的那个索引,在上图的例子中,就是在[0,2,6]中搜索比 4 大的那个索引:
如何用二分查找计算那个恰好比 4 大的索引呢?答案是,寻找左侧边界的二分搜索就可以做到。
注意for (let index in t) index为字符串
function left_bound(arr, target) {
let left=0, right=arr.length-1;
while (left<=right) {
let mid = Math.floor((right-left)/2) + left;
const val = arr[mid]
if(val == target) {
right = mid - 1
} else if(val < target) {
left = mid + 1
} else {
right = mid - 1
}
}
return left;
}
var isSubsequence = function(s, t) {
let dic = {}
for (let index in t) {
let c = t[index]
if (!dic[c]) {
dic[c] = []
}
dic[c].push(index - 0) // index为字符串 转成整型
}
// console.log(dic)
let need = 0
for (let c of s) {
// 不存在字符c
if (!dic[c]) return false;
// 查找字符c在字典的位置
let pos = left_bound(dic[c], need)
// 二分查找没有大于j的下标
if (pos == dic[c].length) return false;
// j在t字符串的下一个开始, 问题所在 上面的index为字符串
need = dic[c][pos] + 1
}
return true;
};
354. 俄罗斯套娃信封问题 (困难)
给我感觉动态规划多一点。先看解析,该题是 动规 + 二分法 ,需要排序后进行处理。
最长递增子序列之信封嵌套问题,这道题目其实是最长递增子序列(Longes Increasing Subsequence,简写为 LIS)的一个变种,因为很显然,每次合法的嵌套是大的套小的,相当于找一个最长递增的子序列,其长度就是最多能嵌套的信封个数。
这道题的解法是比较巧妙的:
先对宽度w进行升序排序(处理了一维),如果遇到w相同的情况,则按照高度h降序排序。之后把所有的h作为一个数组,在这个数组上计算 LIS 的长度就是答案。
将二维问题转化为一维,构造新的序列
二分 + 动规,这样我(连动态规划都没掌握)就先不学了,使用时间复杂度 O(N^2)的动态规划。安慰一下自己。
那就先解决 300. 最长递增子序列 ,给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
因为
d
p
[
i
]
dp[i]
dp[i] 可以有不同的含义,所以跟着文章来。按照一下步骤:
- dp[i] 表示以 nums[i] 这个数结尾的最长递增子序列的长度。
- dp数组全部初始化为 1,因为子序列最少也要包含自己,所以长度最小为 1。
- 状态转移:
L
[
j
]
=
1
+
m
a
x
(
L
[
i
]
)
,
i
<
j
且
a
[
i
]
<
a
[
j
]
L[j]=1+{max(L[i]) , i < j且a[i] < a[j]}
L[j]=1+max(L[i]),i<j且a[i]<a[j]
- 最终结果应该是 dp 数组中的最大值。
LIS代码——leetcode第300题。动态规划,
var lengthOfLIS = function(nums) {
let len = nums.length
let dp = new Array(len).fill(1)
for (let i=1; i<len; i++) {
for (let j=0; j<i; j++) {
if (nums[i] > nums[j])
dp[i] = Math.max(dp[i], dp[j]+1)
}
}
return Math.max(...dp)
};
信封嵌套问题代码——leetcode第354题。动态规划,官方题解都超时,所有语言都是,艹
先记录下,到时在学
var maxEnvelopes = function(envelopes) {
// 按宽度升序排列,如果宽度相同,按高度逆序排列
envelopes.sort((a, b) => {
return a[0]==b[0] ? b[1]-a[1] : a[0]-b[0]
})
let len = envelopes.length
const height = new Array(len)
for (let i = 0; i < len; i++) {
height[i] = envelopes[i][1];
}
// LIS问题
let dp = new Array(len).fill(1)
for (let i=1; i<len; i++) {
for (let j=0; j<i; j++) {
if (height[i] > height[j]) // 只比较高度
dp[i] = Math.max(dp[i], dp[j]+1)
}
}
return Math.max(...dp)
};
793. 阶乘函数后 K 个零 (困难)
相关文章:阶乘相关的算法题
第一反应好像在贪心算法时,做过一道以2,3,5底数作为指针,依次搜索。
末尾是 0 应该与2,5这两个因数有关。
又是先从简单题过度
172. 阶乘后的零
比如说n = 25,那么25!最多可以分解出几个 2 和 5 相乘?这个主要取决于能分解出几个因子 5,因为每个偶数都能分解出因子 2,因子 2 肯定比因子 5 多得多。
问题转化为:n!最多可以分解出多少个因子 5?
难度在于高效计算因子5,
125!,125 第一次除以 5 = 25,说明起码有一个25个数能提高一个因子5
125 除以 25 = 5,说明还有5个数能提高两个因子5,但前面已经提供了一次,剩一个。
125 除以 125,说明还有1个数能提高三个因子5,前面提高了两次,剩一个。
代码:
var trailingZeroes = function(n) {
let ans = 0, base = 5;
while (base <= n) {
ans += Math.floor(n / base)
base *= 5
}
return ans;
};
随着n的增加,trailingZeroes(n!)肯定也是递增的。对于这种具有单调性的函数,用 for 循环遍历,可以用二分查找进行降维打击。
二分查找需要给一个搜索区间
注意:当二分搜索不存在时,left和right的含义
var preimageSizeFZF = function(k) {
let left = 0, right = Number.MAX_SAFE_INTEGER;
// 左侧边界
while (left <= right) {
let mid = Math.floor((right-left)/2) + left;
const val = trailingZeroes(mid)
if(val == k){
right = mid - 1
} else if(val > k){
right = mid - 1
} else{
left = mid + 1
}
}
let first = left;
left = 0, right = Number.MAX_SAFE_INTEGER;
// 右侧边界
while (left <= right) {
let mid = Math.floor((right-left)/2) + left;
const val = trailingZeroes(mid)
if(val == k){
left = mid + 1
} else if(val > k){
right = mid - 1
} else{
left = mid + 1
}
}
// 不存在时 first=x,right=x-1的取值范围
return right - first + 1;
};