算法 | 位运算(哈希思想)

位运算

|----------|----|----------------------------|
| & | 与 | 两个位都为1时,结果才为1**(有0为0)** |
| | | 或 | 两个位都为0时,结果才为0**(有1为1)** |
| ^ | 异或 | 两个位相同为0,相异为1 |
| ~ | 取反 | 0变1,1变0 |
| << | 左移 | 各二进位全部左移若干位,高位丢弃,低位补0 |
| >> | 右移 | 各二进位全部右移若干位,高位补0或符号位补齐 |

判定字符是否唯一

面试题 01.01. 判定字符是否唯一 - 力扣(LeetCode)https://leetcode.cn/problems/is-unique-lcci/description/

题解:

由于题目限制我们不能使用额外的数据结构,我们用位图来解决,位图的原理和哈希表类似。

int i=ch-'a' 来记录字母对应的位图的下标,

  • 如果该下标在位图中为 1,即 bitmap&(1<<i) == 1 ,说明该字母在字符串中不唯一
  • 如果该下标在位图中为 0,则该字母在字符串中是唯一的!则把该位置从 0 改为 1.
cpp 复制代码
class Solution {
public:
    bool isUnique(string astr) {
        if(astr.size()>26) return false;

        int bitmap=0;
        for(auto ch:astr)
        {
            int i=ch-'a';
            if(bitmap&(1<<i)) return false;

            bitmap|=(1<<i);
        }
        return true;
    }
};

丢失的数字

268. 丢失的数字 - 力扣(LeetCode)https://leetcode.cn/problems/missing-number/description/

题解:

异或 -- 相同为0,不同为1

即 1 ^ 1 = 0、0 ^ 0 = 0、1 ^ 0 = 1

由运算规则可知,任何二进制数与零异或,都会等于其本身,即 A ^ 0 = A。


异或性质

(1)交换律: A ^ B = B ^ A

(2)结合律: ( A ^ B ) ^ C = A ^ ( B ^ C )

(3)自反性: A ^ B ^ B = A (由结合律可推: A ^ B ^ B = A ^ ( B ^ B ) = A ^ 0 = A)
因为缺失的数字在这两个数组中只出现了1次,而其余数字都出现了2次,出现2次的数字异或后结果为0。

只要把完整的数组和缺失的数组异或在一起,就可以找到缺失的数字!

cpp 复制代码
class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int n=nums.size();
        int ret=0;
        for(auto x:nums) ret^=x;
        for(int i=0;i<=n;i++) ret^=i;
        return ret;
    }
};

两整数之和

371. 两整数之和 - 力扣(LeetCode)https://leetcode.cn/problems/sum-of-two-integers/description/

题解:

由于不能直接使用加法,只能使用位运算。


把两个数都化为二进制进行相加,二进制相加时,1+1=10,0+0=0,1+0=1,和异或的规则类似,但需要处理进位。

如果两个数都是 1 才需要进位,有一个数不是 1 就不需要进位,这符合 & 的规则。因为进位是向前进 1 位,所以 & 后的结果需要左移 1 位。

把 ^ 和 & 的结果相加起来就可以得到进位后的结果,但是本道题不能使用加法,所以再次 ^ 和 & ,来模拟加法,直到 & 得到的结果为 0,也就是不需要进位时,就可以得到最终结果

cpp 复制代码
class Solution {
public:
    int getSum(int a, int b) {
        while(b!=0)
        {
            int x=(a^b);
            int carry =(a&b)<<1;
            a=x;
            b=carry;
        }
        return a;
    }
};

只出现一次的数字 II

137. 只出现一次的数字 II - 力扣(LeetCode)https://leetcode.cn/problems/single-number-ii/description/

题解:

把数组中所有的数相加,对相加得到的和的某一位数字,有如下 4 种情况:3个0 + 0、3个0 + 1、3个1 + 0、3个1 + 1,把这 4 种情况都模3,就可以得到 0、1、0、1,即只出现一次的数字的对应的二进制。

cpp 复制代码
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret=0;
        for(int i=0;i<32;i++)
        {
            int sum=0;
            for(auto x:nums)
            {
                if((x>>i)&1 == 1) ++sum;
            }
            sum%=3;
            if(sum==1)
                ret |=(sum<<i);
        }
        return ret;
    }
};

消失的两个数字

面试题 17.19. 消失的两个数字 - 力扣(LeetCode)

题解:

这道题类似丢失的数字,只是丢失的是两个数字。

把缺失数字的数组和完整数字的数组异或在一起,除了 a、b只出现了一次,其余数字出现了两次,所以异或的结果其实就是 a^b由于 a、b 是不同的数字,即异或的结果中肯定有某一位的数字为1。

我们找出这一位 x(如果有多位的话,找出其中一位即可),就可以把数组的数字分为两类,一类是 x 位上的二进制为 1,一类是 x 位上的二进制为 0,假设 a 的 x 位上的二进制为 0 ,b 的 x 位上的二进制为 1。

对于 x 位上的二进制为 1 的缺失数字的数组和完整数字的数组,a 只出现一次,其余数字都出现 2次,对于 x 位上的二进制为 0 的 b 也是同理,问题就转换为求一个丢失的数字。

cpp 复制代码
class Solution {
public:
    vector<int> missingTwo(vector<int>& nums) {
        //把所有的数异或在一起
        int tmp=0;
        for(auto x:nums) tmp^=x;
        for(int i=1;i<=nums.size()+2;i++)   tmp^=i;
        //找出a、b中比特位不同的那一位
        int diff=0;
        while(1)
        {
            if(((tmp>>diff) & 1) == 1) break;
            else diff++;
        }
        //根据 diff位的不同,将所有的数划分为两类来异或
        int a=0,b=0;
        for(auto x:nums)
        {
            if(((x>>diff) & 1) ==1) b^=x;
            else a^=x;
        }
        for(int i=1;i<=nums.size()+2;i++)
        {
            if(((i>>diff) & 1) ==1) b^=i;
            else a^=i;
        }
        return {a,b};
    }
};
相关推荐
pianmian14 小时前
python数据结构基础(7)
数据结构·算法
好奇龙猫6 小时前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法
sp_fyf_20247 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-01
人工智能·深度学习·神经网络·算法·机器学习·语言模型·数据挖掘
香菜大丸7 小时前
链表的归并排序
数据结构·算法·链表
jrrz08287 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
oliveira-time8 小时前
golang学习2
算法
南宫生9 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法
懒惰才能让科技进步9 小时前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝
Ni-Guvara9 小时前
函数对象笔记
c++·算法
泉崎10 小时前
11.7比赛总结
数据结构·算法