- 945使数组唯一的最小增量 [1 2 2] -> [1 2 3]
排序 + 贪心策略 - 1014最佳观光组合max(A[i] + A[j] + i - j), i < j
dp[j] – 固定j时的得分;
遍历j,记录[0, j - 1]中A[i] + i的最大值; - 1052爱生气的书店老板 – 窗口X内生气顾客总数, 找到生气顾客最多的窗口位置
初始化[0, X -1]区间内生气客户数;
滑动窗–用grumpy[i] 和grumpy[i - X]作差分更新区间内生气用户和; - 494目标和–在数组中所有元素前添加+/-,返回结果为目标数S的方法数
问题肢解为求部分数和X的方法数 -> 背包思路求解:dp[i][j]表示区间[0, i]上和为j的组合个数
易错点:空集合 和 溢出 以及 初始化 - 523连续的子数组和 – 判断该数组是否含有连续的子数组,其大小至少为 2,总和为 k 的倍数
map<int, vector<int>> – 记录余数0 - (k - 1)对应的数组下标集合;
比较区间[0, i]前缀和 及 区间[0, i+ 2]前缀和,若余数相等,则满足要求 - 795区间子数组个数 – 求连续、非空且其中最大元素满足大于等于L 小于等于R的子数组个数
前提:[0, i]上以i为结尾的连续子数组个数为i + 1;
区间最大值大于等于L小于等于R的子数组个数 = 区间最大值小于等于R的子数组个数-区间最大值小于L的子数组个数 - 327区间和个数 – 返回区间和在 [lower, upper] 之间的个数
遍历i,计算preSum,用multiset<int> s存储数组preSum;
用s.lower_bound(preSum - upper), s.upper_bound(preSum - lower)计算以i为结尾的满足条件的连续子区间个数; - 1109航班预定统计
区间存储 - 1353最多可以参加的会议数目 — 注意:一天只能参加一个会议(不能用区间存储解决)
贪心策略:
class Solution {
public:
int maxEvents(vector<vector<int>>& events) {
/*贪心思想:为了能参加最多的会议,我们尽量每次参加开始时间最早的会议。 注意以下问题:
比如某会议时间是 [1,10],那么我们先尽量尝试第 1 天参加,如果参加了别的会议,再尝试第 2 天...
如果两个会议开始时间相同,我们优先选择结束时间早的那个会议。比如 [1,4] 和 [1,3],利用优先队列,第 1 天先参加 [1,3],第 2 天参加 [1,4]。*/
auto f = [](vector<int> v1, vector<int> v2) -> bool {
if (v1[0] > v2[0]) {
return true;
} else if (v1[0] == v2[0]){
if (v1[1] > v2[1]) {
return true;
} else {
return false;
}
} else {
return false;
}
};
priority_queue<vector<int>, vector<vector<int>>, decltype(f)> pq(f);
for (auto m : events) {
pq.push(m);
}
vector<int> curr = pq.top();
pq.pop();
int last = curr[0];
int k = 1;
while (pq.empty() == false) {
while (pq.size() > 0 && pq.top()[0] <= last) {
vector<int> tmp = pq.top();
pq.pop();
if (last + 1 <= tmp[1]) {
tmp[0] = last + 1;
pq.push(tmp);
}
}
if (pq.size() > 0) {
vector<int> curr = pq.top();
pq.pop();
last = curr[0];
k++;
}
}
return k;
}
};
- 554砖墙 – 你的面前有一堵方形的、由多行砖块组成的砖墙。 这些砖块高度相同但是宽度不同。你现在要画一条自顶向下的、穿过最少砖块的垂线。
注意:该题由于边缘的存在,使得区间存储变得有问题,无法处理特殊case,比如每行砖头个数为1等!
逆向思路:求解穿过边缘的最多函数 -> 用map记录和为sum的行数
class Solution {
public:
int leastBricks(vector<vector<int>>& wall)
{
unordered_map<int, int> hashMap;
for (auto row : wall) {
long long sum = 0;
for (int i = 0; i < row.size() - 1; ++i) {
sum += row[i];
hashMap[sum]++;
}
}
int num = 0;
for (auto each : hashMap) {
num = max(num, each.second);
}
return wall.size() - num;
}
};
- 397整数替换 – 将正整数n变为1的最小替换次数
贪心:除3外,二进制整数最后两位是11则+1,是01则-1 - 1233删除子文件夹
贪心:x 是y的子文件夹,y一定是x的前缀,且 ‘/’ ascii码比小写字母小
先字符串排序:sort(folders.begin(), folders.end()); - 1131绝对值表达式的最大值|arr1[i] - arr1[j]| + |arr2[i] - arr2[j]| + |i - j|
/*因为存在四组两两等价的展开,所以可以优化为四个表达式:
A = arr1[i] + arr2[i] + i
B = arr1[i] + arr2[i] - i
C = arr1[i] - arr2[i] + i
D = arr1[i] - arr2[i] - i
max( |arr1[i] - arr1[j]| + |arr2[i] - arr2[j]| + |i - j|)
= max(max(A) - min(A),
max(B) - min(B),
max© - min©,
max(D) - min(D))
*/
14. 974和可被 K 整除的子数组
负数取余 – 两个关键点: m[0] = 1; m[(sum % K + K) % K]++;
15. 738给定一个非负整数 N,找出小于或等于 N 的最大的整数,同时这个整数需要满足其各个位数上的数字是单调递增
贪心算法,遍历每一位数字,当前位的数字比下一位的数字大,则将该位数字减小1,然后之后位的数字全部变为9
16. 962最大宽度坡
排序,遍历时记录之前的最小的索引
17. 825适龄的朋友总共发送多少份好友请求 – 给定包含有他们年龄的数组
从数组元素值大小入手,用map记录同一年龄段的人数,乘法代替加法,两层for循环则不会超时
18. 826安排工作以达到最大收益 – 每个工人只能被安排一个工作,但一个工作可以被安排多次
两个排序后的数组元素使用双索引,相互比较,有一定代表性
19. 521给定两个字符串 - 最长特殊序列 I – 独有的最长子序列
掌握暴力解法,比特移位 i < (1 << m) (i >> j) & 1) != 0
生成两个字符串所有的子序列共 2^n个,将其存储在 hashmap 中,并记录每个子序列出现的次数。然后找出出现次数为1的最长子序列。如果不存在这样的子序列,返回 -1
20. 522给定两个字符串 - 最长特殊序列 II – 独有的最长子序列
如果存在,一定是列表中的某个字符串 ~~
21. 1043分隔数组以得到最大和 – 给出整数数组 A,将该数组分隔为长度最多为 K 的几个(连续)子数组。分隔完成后,每个子数组的中的值都会变为该子数组中的最大值。
stack 最差, queue + 剪枝最优
22. 870优势洗牌
类似田忌赛马思想!
while 双下标i j, O(n)复杂度,排序B,遍历B[i],判断对应的A[j],如果不满足条件,j++, i保持不变
23. 56合并所有重叠区间
组合排序
24. 128未排序数组中最长连续序列长度 [100, 4, 200, 1, 3, 2] -> [1, 2, 3, 4]
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
if (nums.empty()) return 0;
std::sort(begin(nums),end(nums));
int n=1;
int max=1;
auto last = unique(begin(nums),end(nums));
nums.erase(last, nums.end());
for(int i=1;i<nums.size();i++)
{
if(nums[i]==nums[i-1]+1 )
{
n++;
max=max>n?max:n;
}
else {
n=1;
}
}
return max;
}
};
- 673最长递增子序列的个数
对于任意递增子序列{a1,a2,…,aj,…,ai},求解maxLen[i]时,必有maxLen[j] <= maxLen[j + 1],递推更新maxLen[i]
vector maxLen(len, 1); // maxLen[i] 以nums[i]为结尾的递增子序列最大长度
vector count(len, 1); // count[i]以nums[i]结尾的最长递增子序列个数 - 1186删除一次得到子数组(连续)最大和
f[i] = max(f[i-1] + arr[i], arr[i]);
g[i] = max(g[i-1] + arr[i], f[i-1]); // 表示删除元素的情况下最大连续子数组和,区间[0, i] - 1262可被三整除的最大和
求余,逆向!减去不满足条件的数目 - 1296划分数组为连续数字的集合
用map记录每个数的出现次数 - 1292元素和小于等于阈值的正方形的最大边长
暴力 - 910最小差值 II – 给定一个整数数组 A,对于每个整数 A[i],我们可以选择 x = -K 或是 x = K,并将 x 加到 A[i] 中。在此过程之后,我们得到一些数组 B。
先排序,最小值必然是abs((A[i] + K) - (A[j] -K)), i < j - 798得分最高的最小轮调
/*如果把初始分数看成一个常数S,那么每一次调度都会导致其变化
那么就只考虑调度次数不同情况下的变化即可
那么先记录每个元素调度多少次后开始让分数较少
即从某一步开始,每调度一次就扣一分
那么,最后只需要从标记数组中找到最大的下标即可
*/
class Solution {
public:
int bestRotation(vector<int>& A) {
int n = A.size();
vector<int> score(n, 0);
for (int i = 0; i < n; ++i)
--score[(i + n - A[i] + 1) % n];
for (int i = 1; i < n; ++i)
score[i] += score[i - 1] + 1;
return distance(score.begin(), max_element(score.begin(), score.end()));
}
};
- 611有效三角形的个数
class Solution {
public:
int triangleNumber(vector<int> &nums)
{
if (nums.size() < 3) {
return 0;
}
sort(nums.begin(), nums.end(), greater<int>());
int res = 0;
int N = nums.size();
for (int i = 0; i < N - 2; ++i) {
int l = i + 1;
int r = N - 1;
while (l < r) {
if (nums[l] + nums[r] <= nums[i]) {
--r;
} else {
res += r - l;
++l;
}
}
}
return res;
}
};
- 228汇总区间 – 无重复的有序数组 [0,1,2,4,5,7] ==> [“0->2”,“4->5”,“7”]
整数溢出是个麻烦 , (INT_MAX - 1) + 2 - 1会出错! - 435无重叠区间
tx,区间排序,遍历,比较判断是否删除,保留最远的即可;
dp,dp[i+1]=max(dp[j])+1 - 801使序列递增的最小交换次数 – 数组A、B相同下标元素交换
dp 分别记录A[i - 1]和B[i - 1]交换和不交换时所需最少交换次数s1 和 n1,递推求解s2和n2;不满足条件时,交换次数置为INT_MAX - 1287有序数组中出现次数超过25%的元素
可以发现最终的答案肯定是这三个位置之一:25%长度、50%长度、75%长度,
所以只需要对这三个元素依次用二分法查左右边界,确定左右边界差值是否大于1/4长度 - 907子数组的最小值之和
不是二叉堆!sum[i] = sum[k] + (i - k) * A[i];
维护这样一个栈:stack = {所有元素小于A[k]的数组下标集合,k} - 306累加数(字符串)
用long 和stol才解决,纯粹dfs,路径都不用保存 - 904水果成篮(只有两个篮子,连续子数组)
map[tree[i]]] - 53.最大子序和
优先级队列:priority_queue<int, vector, greater> pq; - 893特殊等价字符串组
用set存储,给string排序 - 402移掉K位数字 – 给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。
if (num[i] < s.top()) s.pop() // 维护一个单调递增的栈,若提前删完,提前退出;若未删干净,从尾部继续删除即可 - 179最大数 – 给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数
字符串比较:
std::sort(strNums.begin(), strNums.end(), [](const string& x, const string& y) {
/* x为后面元素,y为前面元素,return true则将x移动到前面 */
return x + y > y + x;
});
44.718最长重复连续子数组 – 给两个整数数组 A 和 B ,返回两个数组中公共的、长度最长的子数组的长度。
dp[i][j] = dp[i + 1][j + 1] + 1 :从 A[i]和B[j]开始的相同子数组长度
44. 209长度最小的连续子数组
双指针。 left = 0,right = 0,left++,right++
45. 424替换后的最长重复字符 – 找到包含重复字母的最长子串的长度
滑动窗口 或者
暴力法,用map存储每个char对应的下标集合
46. 11盛最多水的容器
双指针
47. 135分发糖果
用两个数组
48. 55跳跃游戏
谁用谁知道,i能到j,就能到i,j之间所有位置
49. 932漂亮数组 – 对于每个 i < j,都不存在 k 满足 i < k < j 使得 A[k] * 2 = A[i] + A[j]
仿射变换
50. 无重复字符的最长子串
方法一:j++,我们找到的没有重复字符的最长子字符串将会以索引 i开头。如果我们对所有的 i这样做,就可以得到答案;
方法二:直接跳过[i, j],置i为j + 1