当前位置: 首页 > article >正文

算法5--位运算

目录

  • 基础
  • 经典例题
    • [面试题 01.01. 判定字符是否唯一](https://leetcode.cn/problems/is-unique-lcci/description/)
    • [268. 丢失的数字](https://leetcode.cn/problems/missing-number/description/)
    • [371. 两整数之和](https://leetcode.cn/problems/sum-of-two-integers/description/)
    • [137. 只出现一次的数字 II](https://leetcode.cn/problems/single-number-ii/description/)
    • [面试题 17.19. 消失的两个数字](https://leetcode.cn/problems/missing-two-lcci/description/)

基础

基本的位运算符有:

<<  >>  ^  ~  &  |

对于这些位运算之间的优先级没有必要记忆,统一加括号即可。
异或操作就是进行无进位相加,有以下规律:

a^0=a
a^a=0
a^b=b^a
a^b^a=b

下面总结一下使用这些位运算符常见的操作:

  1. 求一个整数n的二进制位的第x(0开始)个比特位是0还是1
(n>>x)&1
  1. 将一个整数的二进制位的第x位改为0
n=n&(~(1<<x))
  1. 将一个整数的二进制位的第x位改为1
n=n|(1<<x)
  1. 提取出整数n最右侧的1
n=n&(-n)

在这里插入图片描述
取负数后,最右侧的1所在位置左侧的比特位都取反了,右侧的及当前位置的比特位数保持不变,且右侧位置的比特位全是0

  1. 去除整数n最右侧的1
n=n&(n-1)

在这里插入图片描述
n-1会向n中最右侧的1借1,从而将其置0,该位置左侧的比特位保持不变,右侧比特位全置1,且在n中该位置右侧比特位是全0

  1. 位图

使用一个比特位(也可以多个)标识数据的状态,要根据具体问题实现

经典例题

面试题 01.01. 判定字符是否唯一

实现一个算法,确定一个字符串 s 的所有字符是否全都不同。
0 <= len(s) <= 100
s[i]仅包含小写字母
如果你不使用额外的数据结构,会很加分。

解法一:使用一个哈希表
解法二:使用一张位图,由于只有26个字符,因此使用一个32位整型类型作为位图即可

class Solution {
public:
    bool isUnique(string astr) {
        if(astr.size() > 26){
            return false;
        } 
        int bitMap = 0;
        int i = 0;
        for (; i < astr.size(); ++i) {
            int tmp = 1 << (astr[i] - 'a');
            if (bitMap & tmp) {
                return false;
            }
            bitMap |= tmp;
        }

        return true;
    }
};

268. 丢失的数字

给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。

解法一:利用等差数列求和公式对[0,n]求和得到sum,sum在减去nums的和即可

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        long long int sum1=(nums.size()*(1+nums.size()))/2.0;
        long long int sum2=0;
        for(auto e:nums){
            sum2+=e;
        }

        return sum1-sum2;
    }
};

解法二:利用规律a^a=0,a^0=a,先对[0,n]一一异或得到tmp,再用tmp和nums一一异或即可得到缺失的数字

int missingNumber(int* nums, int numsSize)
{
    int missnum=0;
    int i=numsSize+1;
    while(i--)
    {
        missnum^=i;
    }
    i=numsSize;
    while(i--)
    {
        missnum^=nums[i];
    }

    return missnum;
}

371. 两整数之和

给你两个整数 a 和 b ,不使用 运算符 + 和 - ​​​​​​​,计算并返回两整数之和。

解法1:利用位运算符模拟二进制加法过程

class Solution {
public:
    int getSum(int a, int b) {
        int ans=0;
        int tmp=0;//进位
        int i=0;
        for(;i<32;++i){
            int t1=1&(a>>i);
            int t2=1&(b>>i);
            if((t1||t2||tmp)&&(!((t1&&t2&&!tmp)||(tmp&&t2&&!t1)||(tmp&&t1&&!t2)))){
                ans|=1<<i;
                tmp=0;
            }
            if((t1&&t2)||(t1&&tmp)||(t2&&tmp)){
                tmp=1;
            }
        }

        return ans;
    }
};

解法二:a^b就得到a和b的无进位相加m,(a&b)<<1就得到a和b的进位n,可以令a=m,b=n,重复以上过程,直到进位n为0为止

class Solution {
public:
    int getSum(int a, int b) {
        while(b){
            int m=a^b;
            int n=(a&b)<<1;
            a=m;
            b=n;
        }

        return a;
    }
};

137. 只出现一次的数字 II

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。

记录所有的整数对应的比特位为1的频率,将这些频率进行模3运算,得到的就是只出现一次的整数对应的比特位

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        vector<int> bit(32,0);
        int i=0;
        for(;i<nums.size();++i){
            int j=0;
            for(;j<32;++j){
                bit[31-j]+=1&(nums[i]>>j);
                bit[31-j]%=3;
            }
        }

        int ans=0;
        for(i=0;i<32;++i){
            ans|=bit[i]<<(31-i);
        }

        return ans;
    }
};

面试题 17.19. 消失的两个数字

给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?
以任意顺序返回这两个数字均可。

假设缺的数字是a和b,先对[1,N]异或得到tmp,再用tmp对数组nums一一异或得到的结果就是a^b,由于a和b是不同的两个数字,那么a^b一定有一个比特位为1,假设a^b的第x个比特位为1,现在[0,N]分为第x个比特位为0和为1的两组数字tmp1和tmp2,再将数组nums也分为第x个比特位为0和为1的两组数字nums1和nums2,此时将tmp1和nums1的数字一一异或即可得到a,将tmp2和nums2的数字一一异或即可得到b

class Solution {
public:
    vector<int> missingTwo(vector<int>& nums) {
        int tmp = 0; // a^b
        int i = 0;
        for (i = 1; i <= nums.size() + 2; ++i) {
            tmp ^= i;
        }
        for (i = 0; i < nums.size(); ++i) {
            tmp ^= nums[i];
        }

        // 寻找x
        int x = 0;
        while (x < 32) {
            if (tmp & (1 << x)) {
                break;
            }
            ++x;
        }

        // 分组得到a和b
        int a = 0;
        int b = 0;
        for (i = 1; i <= nums.size() + 2; ++i) {
            if (i & (1 << x)) {
                b ^= i;
            } else {
                a ^= i;
            }
        }
        for (i = 0; i < nums.size(); ++i) {
            if (nums[i] & (1 << x)) {
                b ^= nums[i];
            } else {
                a ^= nums[i];
            }
        }

        return {a, b};
    }
};

http://www.kler.cn/a/473200.html

相关文章:

  • P10424 [蓝桥杯 2024 省 B] 好数
  • Eclipse配置Tomcat服务器(最全图文详解)
  • 关于物联网的基础知识(二)——物联网体系结构分层
  • 路由器的转发表
  • Anthropic 的人工智能 Claude 表现优于 ChatGPT
  • patchwork++地面分割学习笔记
  • 网络安全-kail linux 网络配置(基础篇)
  • NRF24L01模块STM32通信-发送端
  • OA系统如何做好DDOS防护
  • 【Spring Boot】Spring AOP 快速上手指南:开启面向切面编程新旅程
  • 解决Docker冲突问题
  • RabbitMQ高级篇之MQ可靠性 数据持久化
  • 模式识别-Ch2-高斯下判别函数
  • vue.js 路由的基本使用
  • ChatGPT API快速搭建自己的第一个应用—文章摘要(单轮对话应用)
  • Idea日志乱码
  • 进程件通信——网络通信——TCP
  • Flink维表方案选型
  • IOS开发如何从入门进阶到高级
  • 更改IP地址能提高網路速度嗎?
  • conda 批量安装requirements.txt文件
  • MacBook Linux 树莓派raspberrypi安装Golang环境
  • Huawei Cloud EulerOS上安装sshpass
  • VSCode Live Server 插件安装和使用
  • HTTPS 原理详解
  • [Linux]生产消费者模型