算法--位运算和模拟

常见位运算总结

1、基础位运算:^ --> 相同为0,相异为1/ 无进位相加
2、给一个数n,确定它的二进制表示中的第x位是0还是1:(n >> x)&1,结果为0说明第x位是0,结果为1说明第x位为1;或者 n&(1<<x) 结果不为0也可以说明第x位为1
3、将一个数n的二进制表示的第x位改成1:n = n | (1<<x)
4、将一个数n的二进制表示的第x位改成0:n = n &( ~(1<<x))
5、提取一个数n二进制表示中最右侧的1:n = n & (-n),其中-n是将最右侧的1,左边的区域全部变成相反数
6、去除一个数n二进制表示中最右侧的1:n = n & (n-1),其中n-1是将最右侧的1已经右边的部分全部变相反。
7、异或运算的运算律:a ^ 0 = a,a ^ a = 0,a ^ b ^ c = a ^ (b ^ c)。

例题一:两整数之和

1、分析:题目要求简单明了,但是不让我们使用加减符号还要返回两整数之和,那我们可以考虑用异或位运算,不过异或的结果并没有考虑进位,所以我们还要计算出两整数相加的进位情况,这个可以借助与运算来解决,下面给出图示方便理解:

class Solution {
public:
    int getSum(int a, int b) {
        int ret = a^b;
        unsigned int And = ((unsigned int)(a&b))<<1;//(a&b)=-1再左移到符号位属于未定义行为
        while(And)
        {
            int tmp = ret;//下面的And里面的ret不能是更新后的
            ret = ret ^ And;
            And = ((unsigned int)(tmp & And))<<1;
        }
        return ret;
    }
};

例题二:只出现一次的数字II

1、分析:这道题可以用位图的思想尝试解决:因为只有一个数是只出现一次,其他都是三次,那么我们从所有的数结合在一起来看,是不是32位bit位图里,每一个bit位是1的总数一定是3n+1(只出现一次的数的这个bit位是1)或者3n(只出现一次的数的这个bit位是0),那么我们可以遍历nums数组,计算每一个bit位的1的总数目,然后去模3,如果结果是1,是不是可以说明只出现一次的数的这个bit位是1。
2、代码:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        //计算所有数32个比特位每一个比特位的总和,%3==1,single对应比特位为1
        int ret = 0;
        for(int i =0;i<32;i++)
        {
            int sum =0;
            for(auto x:nums)
            {
                //计算1的个数
                if(((x>>i)&1)==1)
                    sum++;
            }
            if(sum%3==1)
            {
                ret = ret|(1<<i);
            }
        }
        return ret;
    }
};

例题三:消失的两个数字

1、这道题虽然是困难题,但是如果做过这两道题:260.只出现一次的数字III 和 268.丢失的数字
那这道题还是很简单的。

将nums数组所有元素和1到nums.size()+2的数字异或得到只出现一次的a^b,因为a和b不相同,一定可以在异或结果找到为1的比特位,再根据这个比特位将所有数分成两部分,分别再异或一次,就可以把a和b给区分开了。

2、代码:

class Solution {
public:
    vector<int> missingTwo(vector<int>& nums) {
        int xorall = 0;
        for(auto e:nums) xorall^=e;
        for(int i =1;i<=nums.size()+2;i++)
        {
            xorall^=i;//缺的两个数的异或结果
        }
        //找到xorall哪一个比特位为1
        int lowbit = 0;
        while(1)
        {
            if(((xorall>>lowbit)&1)==1) break;
            lowbit++;
        }
        int a = 0,b=0;
        //根据lowbit将nums和【1,nums.size()+2】分成两部分
        for(auto e:nums) 
        {
            if(((e>>lowbit)&1)==1) a^=e;
            else b^=e;
        }
        for(int i =1;i<=nums.size()+2;i++)
        {
            if(((i>>lowbit)&1)==1) a^=i;
            else b^=i;
        }
        return {a,b};
    }
};

模拟

例题一:提莫攻击

1、分析:看了示例1大概可以明白这道题需要我们做什么,我们可以抽象成一个数轴:如果b-a>=duration,是不是就是第b秒之前就执行完duration秒,中毒时间要加duration;如果b-a<duration,就相当于没有中毒duration秒就再次中毒,那中毒时间要加b-a秒,之后再从第b秒开始计算中毒时间。最后一次中毒因为不需要判断差值,所以就直接加上duration。

2、代码

class Solution {
public:
    int findPoisonedDuration(vector<int>& timeSeries, int duration) {
        int ret = 0;
        for(int i= 0;i<timeSeries.size()-1;i++)
        {
            int x = timeSeries[i+1]-timeSeries[i];
            if(x>=duration) ret+=duration;
            else ret+=x;
        }
        ret+=duration;
        return ret;
    }
};

例题二:Z字形变换

1、分析:这道题需要我们将输入字符先排成Z字形,然后再从左往右依次读取字符形成新的字符串,如果我们直接进行模拟,就需要创建矩阵,把字符串的字符按照一定顺序放入矩阵,之后再把矩阵的字符一一读取形成新的字符,假设这个矩阵有len列,这样做时间复杂度和空间复杂度是O(n*len)。

我们可以观察这个矩阵得出规律,下面不用具体例子,用下标来模拟上面这个过程,然后再得出规律,看是否能降低时间复杂度和空间复杂度:


上图n=4,观察第一行:是不是第一行的公差是6,这个公差可以由2*n-2得到;最后一行从下标3开始,就是n-1,公差也是6;第二行根据红色连线,应该是先有(1,6-1),然后(1+6,6-1+6)得到(7,11);第三行也是如此。可以初步得出规律:
(1)第一行:0->0+d->0+2d->....
(2)最后一行:n-1->n-1+d->n-1+2d->....
(3)其它行(假设第 i +1行):(i,d-i)->(i+d,d-i+d)->(i+2d,d-i+2d)->....
单看n = 4这个规律可能不够准确,可以再对照n=3,会发现也符合上述规律。
每一行的边界条件都是不要超过输入字符串长度,并且在写其它行代码时的判断条件有两个,可以看代码实现。

class Solution {
public:
    string convert(string s, int numRows) {
        //处理边界情况
        if(numRows==1) return s;
        int d = 2*numRows-2;
        string ret;
        for(int i=0; i<numRows; i++)
        {
            //处理第一行和最后一行
            if(i==0||i==numRows-1)
            {
                int a= 0;
                while(i+a*d<s.size())
                {
                    ret.push_back(s[i+a*d]);
                    a++;
                }
            }
            //处理中间行
            else
            {
                int a= 0;
                while(i+a*d<s.size())
                {
                    ret.push_back(s[i+a*d]);
                    if(d-i+a*d<s.size())
                    ret.push_back(s[d-i+a*d]);
                    a++;
                }
            }
        }
        return ret;
    }
};

例题三:外观数列

1、分析:这道题是需要我们从1到n,每一次都对前一个数组的元素情况进行概括,就比如前一个数组是[3,3,3,1,1,4,5,5],那我们要做的就是遍历这个数组得出有3个3,2个1,1个4和2个5,然后把这些数字3 3 2 1 1 4 2 5放入新数组,直到遍历了n次。那我们是不是可以借助双指针,由left和right来找到含有相同数字的连续区间,并计算个数。
2、代码细节:注意每次遍历前一个数组,right都不能超过前一个数组的大小;还要注意right越界时是没有计算[left,right-1]连续区间的个数的,所以后面还需要再单独计算一次。
 

class Solution {
public:
    string countAndSay(int n) {
        string v1 = "1";
        //执行n-1次
        for(int i = 2;i<=n;i++)
        {
            string tmp;
            int left = 0,right=0;
            while(right<v1.size())
            {
                if(v1[left]==v1[right]) right++;
                else
                {
                    tmp+= to_string(right-left)+v1[left];
                    left = right;
                }
            }
            tmp+= to_string(right-left)+v1[left];
            v1=tmp;
        }
        return v1;
    }
};

例题四:数青蛙

1、分析:这道题我们单看题目和示例可能没有头绪,那现在举个例子借助一个hash表:croakOfFrogs={c r c o a k r o a k c r o a k},从下标0的 c 开始,走到 r 时是不是需要看一下前面有没有 c 的出现,如果出现我们可以让hash表 c 的个数--, r 的个数++;走到 o 时是不是需要看一下前面有没有 r 的出现,如果出现我们可以让hash表 r 的个数--, o 的个数++;也就是说遇到' r o a k '都得往前看,有没有它们对应的前缀字母,如果没有是不是就不符合条件直接返回-1了。对于c 就比较特殊,因为题目是要不同青蛙的最少数目,如果遍历到 c 时,k已经不为0了,可以认为已经有一只青蛙叫完"croak"了,那我是不是可以让它重新再来一次,也就是让hash表里 k 的个数--。
这里遍历完数组,也不能直接return k,还要检查“c r o a”的个数是不是都为0,为0字符串才有效。
2、代码实现:可以直接用if...else...把五个字母分开;也可以用数组模拟hash表,但是需要一个map表记录每个字母和下标对应关系,方便hash直接映射

class Solution {
public:
    int minNumberOfFrogs(string croakOfFrogs) {
        int c=0, r= 0, o = 0,a=0,k=0;
        for(auto e:croakOfFrogs)
        {
            if(e=='c')
            {
                if(k>0)
                {
                    k--;
                    c++;
                }
                else c++;
            }
            else if(e=='r')
            {
                if(c==0) return -1;
                else
                {
                    c--;
                    r++;
                }
            }
            else if(e=='o')
            {
                if(r==0) return -1;
                else
                {
                    r--;
                    o++;
                }
            }
            else if(e=='a')
            {
                if(o==0) return -1;
                else
                {
                    o--;
                    a++;
                }
            }
            else if(e=='k')
            {
                if(a==0) return -1;
                else
                {
                    a--;
                    k++;
                }
            }
        }
        if(c||r||o||a) return -1;
        return k;
    }
};


 

class Solution {
public:
    int minNumberOfFrogs(string croakOfFrogs) {
        string t = "croak";
       vector<int> hash(t.size());
        unordered_map<char,int> map;
        int i = 0;
        //将五个字母和下标映射
        for(auto e:t)
        {
            map[e]= i++;
        }
        for(auto e: croakOfFrogs)
        {
            if(e == 'c')
            {
                if(hash[t.size()-1] > 0) 
                {
                    hash[t.size()-1]--;
                }
                hash[0]++;
            }
            else
            {
                if(hash[map[e]-1] > 0)
                {
                    hash[map[e]-1]--;
                    hash[map[e]]++;
                }
                else return -1;
            }
        }
        for(int i = 0; i < t.size()-1; i++)
        {
            if(hash[i]) return -1;
        }
        return hash[t.size()-1];
    }
};

内容概要:本文介绍了基于AI技术的多功能消防机器人,该机器人适用于室内环境,具备火灾预防功能。它采用STM32单片机控制,配备了K210摄像头、火焰传感器、温湿度传感器等硬件,通过WiFi模块与微信小程序进行远程通信。机器人能够自动巡逻、检测火源环境安全,并通过微信小程序实时向用户发送警告,实现远程监控交互。项目结合了硬件与软件,旨在提高室内消防安全,减少火灾损失。此外,文章详细描述了各个传感器模块的功能及实现原理,如K210摄像头的火焰识别、激光雷达的定位导航等,并展示了部分源代码。适合人群:具备一定嵌入式开发基础,对物联网、AI技术消防机器人感兴趣的工程师研究人员。使用场景及目标:①适用于家庭、工作场所等室内领域,实现火灾预防实时监控;②通过巡逻扫描环境空间与障碍物、定位导航、探测物体/环境进行建模/自动地图测绘、扫描火焰、获取温湿度大小、获取空气安全情况等手段,判断当前环境地点是否安全;③检测到异常时,通过高压水泵喷射器灭火、蜂鸣器报警,并将信息实时发送到用户手机中的微信小程序,实现与用户远程监视交互功能。其他说明:本文不仅提供了详细的硬件软件设计方案,还包括了成本核算源码资料链接,便于读者深入理解实践。项目旨在填补国内室内智能消防领域的空白,提高我国消防现代化水平,保障人们的生命财产安全。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值