力扣刷题4


一、前言

sheepice的刷力扣篇章,虽然现在比较忙,但是如果有空,且写到一些比较有启发的力扣题的时候希望能够发一点点题解,这些题解的灵感来源于我自己看了很多神犇的题解,真的会很有收获。

sheepice的CSDN地址:大家感兴趣也可以去里面,说不定能学到一点东西哦!

二、力扣的某“简单题”

检查整数是否被全部覆盖

这个题目其实说真的,刚开始觉得真的挺难的,然后看到是个简单题我就知道数据的范围肯定给的非常的小,果不其然,数据范围只给了50,哈哈哈,所以第一次做这个题目的时候,这不就是简单的暴力枚举的题目吧,因为这题是三叶姐给到的“-+”题,然后看了看,好家伙,居然有可能上升为一个困难题(三叶姐关于本题的解答),awsl,于是乎我觉得今天去学习一下线段树的基本东西!!

三、一题多解

①暴力哈希

这里其实可以很容易想到,把所有题目中给到的range范围中的数,直接存储下来,然后呢直接进行一次$[left, right]$区间的一个遍历,看看区间内的元素是否满足在区间之内就好了!

class Solution {
public:
    map<int, int> nums;
    bool isCovered(vector<vector<int>>& ranges, int left, int right) {
        for(auto range : ranges) {
            for(auto i = range[0]; i <= range[1]; i++) {
                nums[i]++;
            }
        }
        for(int i = left; i <= right; i++) {
            if(!nums.count(i)) return false;
        }
        return true;
    }
};

暴力解法的时间复杂度为$O(N)$,由于这里开了一个哈希表,所以空间复杂度为$O(N)$。当然这里的数据因为比较小嘛,直接开一个常数的52一个数组去记录数字是否存在,空间复杂度就变成$O(C)$了哈哈。

②差分数组加前缀和

关于差分的思想和前缀和的计算,相信很多同学已经会了,在此不多加赘述,那么这一题的差分思想在哪呢?其实就是我们去计算一个一个区间所存在数字的时候.先设置一个差分数组$diff[52]$,假设我们要计算区间$[1,10]$之内,保证这之间的数都出现过也就不为0,那么我们只需要让$diff[1]+1$然后让$diff[11]-1$,之后对区间做一次前缀和我们可以发现区间$[1,10]$内的$diff$数组就会全部变成1,代表数字出现在区间内,这样虽然并没有大大的优化时间复杂度。但是我们不难发现,如果当题目所给的区间有重复的时候,我们是可以通过这样的方法去计算出每一个数字被重复的次数,也就是在整个区间里面重复出现了多少次,这个可能也对日后碰到这样的题目提供了一个非常不错的思路!

class Solution {
public:
    int diff[52];
    bool isCovered(vector<vector<int>>& ranges, int left, int right) {
        for(auto range : ranges) {
            diff[range[0]]++;
            diff[range[1] + 1]--;
            }
        for(int i = 1; i < 52; i++) {
            diff[i] += diff[i - 1];
        }
        for(int i = left; i <= right; i++) {
            if(diff[i] <= 0) return false;
        }
        return true;
    }
};

时间复杂度为$O(N)$,空间复杂度就变成$O(C)$。

③树状数组

本题采用线状数组其实无非就是会和第二种解法一样,只不过在树状数组的add操作里面,每次加入的是代表此元素出现的次数,最后利用差分的思想,可以直接得到某元素出现的次数,其实和方法二大同小异,但是希望自己再练一遍树状数组,所以呢,就还是写了一遍代码。如果对树状数组不了解的同学,sheepice也写了一篇博客,仅供uu们进行参考.树状数组初探!!

class Solution {
public:
    int n = 55;
    int sum[55];
    int lowbit(int x) {
        return x & (-x);
    }
    void add(int index, int value) {
        for(int i = index; i <= n - 1; i += lowbit(i)) {
            sum[i] += value;
        }
    }
    int query(int index) {
        int ans = 0;
        for(int i = index; i > 0; i -= lowbit(i)) {
            ans += sum[i];
        }
        return ans;
    }
    bool isCovered(vector<vector<int>>& ranges, int left, int right) {
        for(auto range : ranges) {
            for(auto i = range[0]; i <= range[1]; i++) {
                add(i, 1);
            }
            }
        for(int i = left; i <= right; i++) {
            if(query(i) - query(i - 1) <= 0) return false;
        }
        return true;
    }
};

xxxxxxxxxx //c++版本的解答class Solution {public:    int maxProduct(vector& words) {        int n = words.size();        vector dp(n,0);        for(int i = 0; i < n; i++) {            for(int j = 0; j < words[i].size(); j++) {                char u = words[i][j];                //进行每一位1的存储                dp[i] |= (1 << (u - ‘a’));             }       }        int ans = 0;        //从头比较到尾,满足无重复数字就进行相关的答案记录        for(int i = 0; i < n - 1; i++) {            for(int j = i+1; j < n; j++) {                if((dp[i] & dp[j]) == 0) {//这里取最大值的地方可以注意一下//可以用ans = max(ans, (int)(words[i].size() * words[j].size()));//因为.size()结构是返回无符号类型的int所以力扣上会报错!!!                    if(words[i].size() * words[j].size() > ans)                    ans = words[i].size() * words[j].size();               }           }       }        return ans;   }};c++

④线段树

线段树应该是解决所有树状数组能够解决的一些相关问题,同时也是解决绝大部分的区间求和和查询的一个最有利的手段,有关线段树的学习,大家可以参考此篇:线段树和树状数组。本篇下面的代码仅供参考,因为sheepicce今天才稍微懂一点线段树,预计这周会总结一下线段树的一些东西哦!

const int N = 55;
class Solution {
public:
struct Node {
int l, r, cnt;
};
Node tr [N * 4];
void pushup(int u) {
tr[u].cnt = tr[u << 1].cnt + tr[u << 1 | 1].cnt;
}
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r, tr[u].cnt = 0;
if(l != r)
{
int mid = (l + r) >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
// 从 tr 数组的下标 u 开始,在数值 x 的位置进行标记
void update(int u, int x) {
if (tr[u].l == x && tr[u].r == x) {
tr[u].cnt = 1;
} else {
int mid = (tr[u].l + tr[u].r) >> 1;
if (x <= mid) update(u << 1, x);
else update(u << 1 | 1, x);
pushup(u);
}
}
// 从 tr 数组的下标 u 开始,查询 [l,r] 范围内有多少个数值被标记
int query(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) return tr[u].cnt;
int mid = (tr[u].l + tr[u].r) >> 1;
int ans = 0;
if (l <= mid) ans += query(u << 1, l, r);
if (r > mid) ans += query(u << 1 | 1, l, r);
return ans;
}
bool isCovered(vector <vector > & rs, int l, int r) {
build(1, 1, N);
for (auto & cur : rs) {
int a = cur[0], b = cur[1];
for (int i = a; i <= b; i++) {
update(1, i);
}
}
int tot = r - l + 1 , cnt = query(1, l, r);
return tot == cnt;
}
};

四、总结

总之,虽然是一个简单的题目,但是还是有很多可以拓展的地方,这也是我需要慢慢去学习的!加油啊!冲鸭!


文章作者: sheepice
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 sheepice !
  目录