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

位运算算法:解锁高效编程的钥匙

常见位运算场景:


5.消失的两个数字


 






1.判定字符是否唯一

解法一:使用HashSet 

借助 HashSet 存储字符。HashSet 不允许有重复元素,在遍历字符串时尝试添加字符,若添加失败就表明有重复字符,返回 false;若遍历完都没出现添加失败的情况,则返回 true

class Solution {
    public boolean isUnique(String astr) {
        //大优化
        if(astr.length() > 26) return false;//因为只包含小写字母
        Set<Character> set = new HashSet<>();
         // 创建一个 HashSet 来存储字符
        char[] s = astr.toCharArray();
        for(char c:s){
           if(!set.add(c)) return false;// 如果添加失败,说明字符已经存在于集合中
        }
        return true;

    }
}

 

解法二:使用位图

我们从位图可以表示是否出现过

class Solution {
    public boolean isUnique(String astr) {
        int bitmap = 0;
        if(astr.length() > 26) return false;//进行优化
        for(int i = 0;i<astr.length();i++){
            int x = astr.charAt(i) - 'a';
            if(((bitmap>>x) & 1) == 1) return false;//使用了检查x位置是否为1

            bitmap = 1<<x | bitmap;//使用了将x位置修改成1
        }
        return true;
        
    }
}

 

2.丢失的数字

解法一:使用Set容器(时间复杂度 O(n),空间复杂度O(n))

创建一个 HashSet 对象 set,用于存储数组中的元素,遍历数组 nums,将每个元素添加到 set 中,遍历从 0 到 n 的所有数字,检查 set 中是否包含该数字。如果不包含,则返回该数字。

class Solution {
    public int missingNumber(int[] nums) {
        //1.使用哈希表
        int n = nums.length;//定义长度
        Set<Integer> set = new HashSet<>();//定义一个Set容器
        for(int num:nums){
            set.add(num);
        }//遍历存放

        //找出那个没有存放元素的下标并返回
        for(int i = 0;i <= n ;i++){
            if(!set.contains(i)) return i;
        }
        return -1;

    }
}

解法二:高斯求和(时间复杂度O(1) )

解题思路:我们可以先根据数组长度 n 计算出从 0 到 n 的所有数字的总和,然后计算数组中所有元素的和,两者相减就能得到缺失的数字。 

class Solution {
    public int missingNumber(int[] nums) {
        //高斯求和
        int n = nums.length;
        int totalSum = n * (n + 1) / 2;//区间所有元素和
        int numsSum = 0;
        for(int x : nums) numsSum+=x;//数组和
        return totalSum - numsSum;//两数只差就是缺失的数字
    }
}

解法三:使用位运算 (使用异或(^)

异或的性质: 

  1. 归零律:任何数和自身进行异或运算,结果为 0,即 a ^ a = 0。例如,5 ^ 5 = 0
  2. 恒等律:任何数和 0 进行异或运算,结果是其本身,即 a ^ 0 = a。例如,5 ^ 0 = 5
  3. 交换律和结合律:异或运算满足交换律和结合律,也就是 a ^ b ^ c = a ^ c ^ b = b ^ a ^ c 等。

 规律:

  1. 对数组中的所有元素进行异或运算,得到ret
  2. 然后ret遍历[0 ,n],类似消消乐,因为第一次异或后,第二次又去异或会得到0
  3. 最后0会异或缺失的数字,得到缺失数字本身

class Solution {
    public int missingNumber(int[] nums) {
        int ret = 0;
        for(int num : nums) ret ^= num;
        for(int i = 0;i<=nums.length;i++) ret^=i;
        return ret;
        
    }
}


3.两整数之和 

解法:使用位运算 

比如正常使用二进制计算加法

但是,计算机不能直接一步到位计算(它不能进位相加)

  1. 首先使用(异或)^,进行无进位相加
  2. 使用&左移1位,找出要进位的1(为什么要左移一位?,因为&后,1是在原位,没有进位)
  3. 不断重复以上步骤,直到进位数为0 

 

class Solution {
    public int getSum(int a, int b) {
        
        while(b != 0){
             int x = a ^ b;//算出无进位相加的结果
             int y = (a & b) << 1;//计算进位
             a = x;
             b = y;
        }
        return a;
    }
}

 


4.只出现一次的数字 II

解法:使用位图

 

  • 数组中,每一个数的对应的比特位要么为1,要么为0
  • 比特位存在上述四种情况
  • 把所有数字的对应的比特位相加,模 3 后得到的刚好知道只出现一次数字对应的比特位是多少(因为a是单个)
  • 然后如果是比特位是1,那就修改对应的比特位为1(从数字0)
  • 修改完之后,得到的二进制位就是只出现一次的数字

不是很难,用脑子回顾一下这个过程

class Solution {
    public int singleNumber(int[] nums) {
        int ret = 0;
        for(int i = 0;i < 32;i++){
            int sum = 0;
            for(int x : nums){
                if(((x>>i) & 1 ) == 1) sum++;//检查是否为1,并累积sum
            }

            if(sum % 3 == 1){
                ret = (1<<i) | ret;//如果为1,就修改当前比特为1
            }
                
            
        }
        return ret;
        
    }
}

 


5.消失的两个数字

解法:位运算

 先认识关于异或^的概念(关于缺失的数字的概念)

解题步骤:

1.先把nums 和 [1 ,length + 2 ]所有的数异或在一起,就会得到两个缺失的数字进行异或。

2.找出a,b两个比特位不同的那一位(一个数字比特位为0,一个数字比特位为1)

3.将所有的数按照diff位不同,分异或两类 

 代码:
 

class Solution {
    public int[] missingTwo(int[] nums) {
        //1.先把所有的数异或在一起
        int tmp = 0;
        for(int x :nums) tmp^=x;
        for(int i = 1;i <= nums.length+2;i++ ){
            tmp^=i;
        }
        //2.找出a,b两个比特位不同的那一位
        int diff = 0;
        while(true){
            if(((tmp>>diff) & 1) == 1) break;
            else diff++;
        }
        //3.将所有的数按照diff位不同,分异或两类
        int ret[] = new int[2];
        for(int x:nums)
            if(((x>>diff) & 1) == 1) ret[1] ^=x;
            else ret[0] ^= x;

       for(int i = 1;i <= nums.length+2;i++ ){
            if(((i>>diff) & 1) == 1) ret[1] ^=i;
            else ret[0] ^= i;
        }
        return ret;
    }
}

 

 


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

相关文章:

  • 【C#】`Interlocked` vs `lock`
  • debug 笔记:llama 3.2 部署bug 之cutlassF: no kernel found to launch!
  • 歌词json
  • 【redis】持久化之RDB与AOF
  • [c语言日寄]柔性数组
  • [Java微服务架构]7-3_事务处理——分布式事务
  • UML之包含用例
  • 基于 Qt / HTTP/JSON 的智能天气预报系统测试报告
  • 智慧电力:点亮未来能源世界的钥匙
  • 推荐:大模型靠啥理解文字?通俗解释:词嵌入embedding
  • 网络安全法律法规简介
  • RFID技术在机器人中的核心应用场景及技术实现
  • AI PPT哪家强?2025年4款高效工具深度测评
  • 【数据分享】基于联合国城市化程度框架的全球城市边界数据集(免费获取/Shp格式)
  • 对匿名认证的理解
  • [Java微服务架构]7-2_事务处理——全局事务与共享事务
  • Python每日一题(7)
  • springboot 四层架构之间的关系整理笔记五
  • 怎样进行服务器的日常安全监控和审计?
  • uniapp用户登录及获取用户信息(头像昵称)