leetcode刷题总结 -- O(n)问题

  1. 945使数组唯一的最小增量 [1 2 2] -> [1 2 3]
    排序 + 贪心策略
  2. 1014最佳观光组合max(A[i] + A[j] + i - j), i < j
    dp[j] – 固定j时的得分;
    遍历j,记录[0, j - 1]中A[i] + i的最大值;
  3. 1052爱生气的书店老板 – 窗口X内生气顾客总数, 找到生气顾客最多的窗口位置
    初始化[0, X -1]区间内生气客户数;
    滑动窗–用grumpy[i] 和grumpy[i - X]作差分更新区间内生气用户和;
  4. 494目标和–在数组中所有元素前添加+/-,返回结果为目标数S的方法数
    问题肢解为求部分数和X的方法数 -> 背包思路求解:dp[i][j]表示区间[0, i]上和为j的组合个数
    易错点:空集合 和 溢出 以及 初始化
  5. 523连续的子数组和 – 判断该数组是否含有连续的子数组,其大小至少为 2,总和为 k 的倍数
    map<int, vector<int>> – 记录余数0 - (k - 1)对应的数组下标集合;
    比较区间[0, i]前缀和 及 区间[0, i+ 2]前缀和,若余数相等,则满足要求
  6. 795区间子数组个数 – 求连续、非空且其中最大元素满足大于等于L 小于等于R的子数组个数
    前提:[0, i]上以i为结尾的连续子数组个数为i + 1
    区间最大值大于等于L小于等于R的子数组个数 = 区间最大值小于等于R的子数组个数-区间最大值小于L的子数组个数
  7. 327区间和个数 – 返回区间和在 [lower, upper] 之间的个数
    遍历i,计算preSum,用multiset<int> s存储数组preSum;
    用s.lower_bound(preSum - upper), s.upper_bound(preSum - lower)计算以i为结尾的满足条件的连续子区间个数;
  8. 1109航班预定统计
    区间存储
  9. 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;
     }
};
  1. 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;
    }
};
  1. 397整数替换 – 将正整数n变为1的最小替换次数
    贪心:除3外,二进制整数最后两位是11则+1,是01则-1
  2. 1233删除子文件夹
    贪心:x 是y的子文件夹,y一定是x的前缀,且 ‘/’ ascii码比小写字母小
    先字符串排序:sort(folders.begin(), folders.end());
  3. 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;
    }
};
  1. 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]结尾的最长递增子序列个数
  2. 1186删除一次得到子数组(连续)最大和
    f[i] = max(f[i-1] + arr[i], arr[i]);
    g[i] = max(g[i-1] + arr[i], f[i-1]); // 表示删除元素的情况下最大连续子数组和,区间[0, i]
  3. 1262可被三整除的最大和
    求余,逆向!减去不满足条件的数目
  4. 1296划分数组为连续数字的集合
    用map记录每个数的出现次数
  5. 1292元素和小于等于阈值的正方形的最大边长
    暴力
  6. 910最小差值 II – 给定一个整数数组 A,对于每个整数 A[i],我们可以选择 x = -K 或是 x = K,并将 x 加到 A[i] 中。在此过程之后,我们得到一些数组 B。
    先排序,最小值必然是abs((A[i] + K) - (A[j] -K)), i < j
  7. 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()));
	}
};
  1. 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;
    }
};
  1. 228汇总区间 – 无重复的有序数组 [0,1,2,4,5,7] ==> [“0->2”,“4->5”,“7”]
    整数溢出是个麻烦 , (INT_MAX - 1) + 2 - 1会出错!
  2. 435无重叠区间
    tx,区间排序,遍历,比较判断是否删除,保留最远的即可;
    dp,dp[i+1]=max(dp[j])+1
  3. 801使序列递增的最小交换次数 – 数组A、B相同下标元素交换
    dp 分别记录A[i - 1]和B[i - 1]交换和不交换时所需最少交换次数s1 和 n1,递推求解s2和n2;不满足条件时,交换次数置为INT_MAX
  4. 1287有序数组中出现次数超过25%的元素
    可以发现最终的答案肯定是这三个位置之一:25%长度、50%长度、75%长度,
    所以只需要对这三个元素依次用二分法查左右边界,确定左右边界差值是否大于1/4长度
  5. 907子数组的最小值之和
    不是二叉堆!sum[i] = sum[k] + (i - k) * A[i];
    维护这样一个栈:stack = {所有元素小于A[k]的数组下标集合,k}
  6. 306累加数(字符串)
    用long 和stol才解决,纯粹dfs,路径都不用保存
  7. 904水果成篮(只有两个篮子,连续子数组)
    map[tree[i]]]
  8. 53.最大子序和
    优先级队列:priority_queue<int, vector, greater> pq;
  9. 893特殊等价字符串组
    用set存储,给string排序
  10. 402移掉K位数字 – 给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。
    if (num[i] < s.top()) s.pop() // 维护一个单调递增的栈,若提前删完,提前退出;若未删干净,从尾部继续删除即可
  11. 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自由技艺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值