力扣刷题笔记2023.1.5和1.6

力扣刷题笔记

力扣每日一题1.5 统计异或值在范围内的数对有多少



前言

力扣每日一题1.5(hard),统计异或值在范围内的数对有多少,题目:

给你一个整数数组nums(下表从0开始计数)以及两个整数low和high,请返回漂亮数对的数目。
漂亮数对是一个形如(i,j)的数对,其中0 <= i < j < nums.length且 low <= (nums[i] XOR nums[j]) <= high。
1 <= nums.length <= 2 * 10^4
1 <= nums[i] <= 2 * 10^4
1 <= low <= high <= 2 * 10^4


一、题目分析

本题的解题思路目前比较简单,就是按照题意,遍历数组,看两两组队的元素数对是否符合漂亮数对

二、解题方法

method1:遍历数组,检查每两个元素数据对是否符合漂亮数对的条件

注意:位运算符(亦或^)优先级比比较运算符(<=)优先级低,因此需要注意括号的使用

代码如下:

class Solution {
public:
    int countPairs(vector<int>& nums, int low, int high) {
        int size = nums.size();
        int res = 0;
        for(int i = 0;i < size - 1;i++){
            for(int j = i + 1;j < size;j++){
               // cout << (nums[i] ^ nums[j]) << endl;
                if((nums[i] ^ nums[j]) >= low && (nums[i] ^ nums[j]) <= high){
                    res++;
                }
            }
        }
        return res;
    }
};

提交后超过时间限制,超过时间限制截图如下图所示:时间复杂度为O(n^2),n为数组的长度,因此需要降低时间复杂度。
超时截图
因此,method1方法不能采用

method2.字典树

template<class T,size_t N> class array;,c++中array的通用格式为array<类型名,元素个数>数组名
由于之前对字典树的使用比较少,因此在这里详细分析一下
先对于时间复杂度的分析,由于nums <= 2* 10^4,因此总共只需要15位二进制数即可满足( 2^14=16384 ;2^15=32768)
本方法的思路:

  1. 判断ai和aj是否属于漂亮数对是看ai和aj的亦或值是否在low和high之间,字典树判断是否在一个范围之间比较复杂,因此判断数组中两个元素的异或值<=high的个数减去异或值<low的个数比较简单。
    分解后的的问题:判断某个数组中两个元素的异或值小于给定数值的个数,如若满足,则称为美丽数对
  2. 对于分解后的问题的解决方案(利用字典树来解决):
    首先:从第二个元素开始,判断该元素与每个之前的元素是否满足美丽数对,
    之所以从第二个元素开始,是方便将元素一个个的插入字典树中,即先插入第一个元素,再插入第二个......最后插入倒数第二个元素到字典树中
    其次构造字典树,即字典树每个结点有两个属性值,一个是指向子结点的数组指针(分别代表下一位是0还是1)以及目前已插入结点拥有该前缀的元素个数sum。
    最后,构造两个方法,分别为add方法,即将元素插入字典树中;以及get方法,即针对某一元素ai,看ai之前的元素与ai是美丽数对的个数。
  3. 合并问题,将每个元素ai是美丽数对的个数累加。最后再用high的美丽数对的个数减去low-1的美丽数对的个数,即可求出最后需要得出的值。
    可以看出,该方法的时间复杂度为O(n*logc),n为数组的长度,c为数组单个元素的大小,即15 > logc > 0,可以得出该方法和method1比起来时间复杂度降低一个量级

代码如下:

//构建字典树结点结构
struct Tire{
    //初始化每个结点的指针数组,child[0]代表左子树,child[1]代表右子树
    array<Tire*,2> child{nullptr,nullptr};
    int sum;
    //等同于Tire():sum(0){}
    Tire(){sum = 0;}
};
class Solution {
private:
    //字典树的根结点
    Tire* root = nullptr;
    //定义静态常量,最高位为14位(位数从0开始)
    static constexpr int HIGHBIT = 14;

public:
    void add(int elementVal){
        Tire* cur = root;//获取根结点指针
        for(int k = HIGHBIT;k >= 0;k--){
            int bit = (elementVal >> k) & 1;
            if(cur -> child[bit] == nullptr){
                cur->child[bit] = new Tire();
            }
            cur = cur->child[bit];
            cur->sum++;
        }
    }
    //注意该函数只是针对某一个ai
    int get(int num,int x){
        Tire* cur = root;
        int sum = 0;
        //遍历的是位数
        for(int k = HIGHBIT;k >= 0;k--){
            int r = (num >> k) & 1;
            if((x >> k) & 1){
                if(cur->child[r] != nullptr){
                    sum += cur->child[r]->sum;
                }
                if(cur->child[r ^ 1] == nullptr){
                    return sum;
                }
                cur = cur->child[r ^ 1];
            }else{
                if(cur->child[r] == nullptr){
                    return sum;
                }
                cur = cur->child[r];
            }
        }
        sum += cur->sum;
        return sum;
    }

    int f(vector<int>& nums,int x){
        //初始化根结点,根结点的sum无用
        root = new Tire();
        int res = 0;
        for(int i = 1;i < nums.size();i++){
            //从小到大,把元素加入字典树中,即判断a[i]和(a[0]到a[i-1])是否是漂亮数对
            add(nums[i-1]);
            //对于每个ai,判断有多少个漂亮数对(ai,aj)(j < i)
            res += get(nums[i],x);
        }
        return res;
    }
    int countPairs(vector<int>& nums, int low, int high) {
        //f函数表示在第一个参数nums数组中多少对元素的亦或运算结果小于等于第二个参数
        return f(nums,high) - f(nums,low-1);
    }
};

三、每日一题1.6

题目:统计各位数字之和为偶数的整数个数(easy)
暴力枚举即可,代码如下所示:

class Solution {
public:
    //各位是指个、十、百、千位
    bool isOushu(int num){
        int tempNum = 0;
        while(num != 0){
            tempNum += num % 10;
            num = num/10;
        }
        if(tempNum % 2 == 0){
            return true;
        }
        return false;
    }
    int countEven(int num) {
        int res = 0;
        for(int i = 1;i <= num;i++){
            if(isOushu(i)){
                res++;
            }
        }
        return res;
    }
};

总结

1.5的每日一题难度较大,字典树的使用也比较陌生,后续仍需加强对该方面题型的学习和巩固;1.6比较简单,之间暴力枚举即可解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值