力扣刷题4

一、前言

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

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

二、力扣的某“简单题”

检查整数是否被全部覆盖

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

三、一题多解

①暴力哈希

这里其实可以很容易想到,把所有题目中给到的range范围中的数,直接存储下来,然后呢直接进行一次 [ l e f t , r i g h t ] [left, right] [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) O(N),由于这里开了一个哈希表,所以空间复杂度为 O ( N ) O(N) O(N)。当然这里的数据因为比较小嘛,直接开一个常数的52一个数组去记录数字是否存在,空间复杂度就变成 O ( C ) O(C) O(C)了哈哈。

②差分数组加前缀和

关于差分的思想和前缀和的计算,相信很多同学已经会了,在此不多加赘述,那么这一题的差分思想在哪呢?其实就是我们去计算一个一个区间所存在数字的时候.先设置一个差分数组 d i f f [ 52 ] diff[52] diff[52],假设我们要计算区间 [ 1 , 10 ] [1,10] [1,10]之内,保证这之间的数都出现过也就不为0,那么我们只需要让 d i f f [ 1 ] + 1 diff[1]+1 diff[1]+1然后让 d i f f [ 11 ] − 1 diff[11]-1 diff[11]1,之后对区间做一次前缀和我们可以发现区间 [ 1 , 10 ] [1,10] [1,10]内的 d i f f diff 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(N) O(N),空间复杂度就变成 O ( C ) O(C) 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;
    }
};

时间复杂度为 O ( N ) O(N) O(N),空间复杂度变成 O ( C ) O(C) O(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;
}
};

四、总结

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

sheep.ice

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

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

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

打赏作者

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

抵扣说明:

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

余额充值