1038 字
5 分钟
第165次力扣双周赛
这里先挂一个链接
有兴趣的可以去做一下原题
事先声明一下,咱这里不是最优解,只不过做题的时候可以想想这个思路
第一题:大于平均值的最小未出现正整数
根据题目意思,我们要做的事情可以分为以下几点:
- 需要计算所有元素的平均值
- 需要找到一个数,要求:
- 需要严格大于平均值
- 没有在数列中出现过
- 需要是个正整数
这里是思路的推理,想要直接看代码的可以点这里
最初的想法是可以先直接计算数列平均值,得出期望的最小值,然后去查找排序队列中是否有该值,如果有那就+1
这个想法在本题是可以通过的,但是仔细想想🤔是有很多可以优化的地方的:
- 对于计算平均值,这个确实需要遍历完一整个数组O(N)
- 对于得出期望最小值,只需要计算adv+1即可,是O(1)的
- 本题主要优化的是查找排序队列中是否存在此值的问题:
- 首先就是可以用hash表(或者布尔数组)去记录一下是否存在该值,如果该值存在则+1,这个解决了需要连续遍历数组的问题将时间变为了O(N+K),但是同时衍生了一个问题会出现O(N)的空间开销
K为在最小期望值后的连续存在的正整数的数量
- 再是复杂的角标处理的问题,无论用不用哈希表都需要去一个个访问容器内的元素来确认是否存在
说了这么久,终于要请出本次的解决方案了位运算 对于以上的问题,位运算的的优势如下:
- 有等效的去重,查询效果
- 解决了哈希\布尔容器的内存占用大的问题
- 有可以快速获取第一个非0的方法
解题步骤
-
我们对于每一个容器内的元素I,同样都进行累加和记录,并且计算期望最小值
int sum = 0;bitset<10000> bit;for (int i = 0; i < size; i++) {sum+=nums[i];if (nums[i]>0) //这里要注意的是,我们只记录正半区,因为当adv为负的时候,需要检查的也仅仅只是从1开始的正半区bit[nums[i]] = true;}int adv = sum / size + 1; //这里不+1为平均值的取整数部分,对于期望值而言,无论adv是整数还是小数,都需要比他大然后取整,所以直接在adv上+1则为期望最小值 -
计算偏移量 对于我们要的最终最小值,其结果分为两种情况: 偏移量 = 距离下一个可用的0的距离
- 当adv > 0时 先将此时的bit >> adv然后再开始计算偏移量 此时的最终最小值=期望最小值+偏移量
- 当adv < 0时 此时的最终最小值=从正整数1开始的偏移量 所以需要跳过bit[0] 则代码如下
if (adv >0) //第一种情况bit >>= adv;else { //第二种情况跳过bit[0]bit.set(0);}int result = 0;bit = ~bit;result += bit._Find_first(); //巧用库函数来获取第一个不为0的值return adv > 0 ? result + adv : result;
完整代码如下:
class Solution { public: int smallestAbsent(vector<int>& nums) {
int sum = 0; bitset<10000> bit; int size = nums.size(); for (int i = 0; i < size; i++) { sum+=nums[i]; if (nums[i]>0) bit[nums[i]] = true; } int adv = sum / size + 1; if (adv >0) bit >>= adv; else { bit.set(0); } int result = 0; bit = ~bit; result += bit._Find_first(); return adv > 0 ? result + adv : result; }};复杂度分析
- 时间复杂度
(N)其中 n 是数组 nums 的长度。遍历数组将元素加入bitset的时间是 O(n),计算答案时需要遍历的元素个数 < n,因此时间复杂度是 O(n) - 空间复杂度
(K)其中 k 是数组中的值的最大值/8的结果。创建的bitset的大小为k+1