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

位运算算法题

一.判断字符是否唯一

 法一:

        我们直接借助一个字符数组来模拟哈希表统计字符串即可,并且我们没有必要先将所有字符都放入字符数组中,边插入边判断,当我们要插入某个字符的时候,发现其已经出现了,此时必然重复,直接返回false。

        时间复杂度:O(n),最坏情况下只需要遍历一次字符串即可

        空间复杂度:O(1),我们只是用了固定的常数级的额外空间:26个char的字符数组

法二:

        因为字符串只包含26个小写字母。我们可以用一个int变量的26个bit位来依次表示26个小写字母。0表示没出现,1表示出现,如果遍历到那个字符,发现其对应的二进制位为1,表示该字符重复出现,返回false。

        我们首先定义一个int变量mark来记录字符的出现次数。接着我们遍历字符串,我们让字符a对应0位置,以此类推。遍历字母的时候,先判断对应位是0还是1( (x>>i) & 1 ),如果是1直接返回,如果是0,则将该位修改成1( x |= ( 1<< i ) )

        时间复杂度:O(n),最坏情况下只需要遍历一遍字符串即可

        空间复杂度:O(1),只使用了一个int变量来标记字母

细节:如果字符串的长度大于26,则该字符串一定存在重复字符。直接返回false。

// C++
// 法一:

class Solution 
{
public:
    bool isUnique(string astr) 
    {
        int hash[26] = {0};
        for(auto e : astr)
        {
            if(hash[e-'a'] == 1) return false;
            else hash[e-'a'] = 1;
        }
        return true;
    }
};

// 法二:
class Solution 
{
public:
    bool isUnique(string astr) 
    {
        if(astr.size() > 26) return false;
        int mark = 0;
        for(auto e : astr)
        {
            int index = e-'a';
            if(((mark >> index) & 1) == 0) mark |= (1<<index);
            else return false;
        }
        return true;
    }
};
# Python
# 法二:

class Solution:
    def isUnique(self, astr: str) -> bool:
        if len(astr) > 26 :
            return False
        mark = 0
        for e in astr:
            index = ord(e) - ord('a')
            if ((mark >> index) & 1) == 1 :
                return False;
            else:
                mark |= (1<<index)
        return True

二.丢失的数字

 0~n这n+1个数中,有n个数放在了数组中,我们要找出那缺少的那个数

解法:

        我们借助异或运算符的性质:a^a = 0,a^0 = a,a^b^a = (a^a)^b.

        我们可以先将数组的元素都异或,然后在将0~n这n+1个数再异或,这样整个数据集就满足只有一个数出现了一次,其余都出现了两次。这样重复的数就可以通过异或操作变为0,最后剩下的数就是丢失。

        时间复杂度:O(n),我们只需要遍历两遍数组,即可得到异或结果

        空间复杂度:O(1),我们只是用了一个int变量来存储异或的结果。

// C++

class Solution 
{
public:
    int missingNumber(vector<int>& nums) 
    {
        int mark = 0;
        for(auto e : nums) mark ^= e;
        for(int i=0; i<=nums.size(); ++i) mark ^= i;
        return mark;
    }
};
# Python

class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        mark = 0
        for i in nums:
            mark ^= i
        for i in range(0,len(nums)+1):
            mark ^= i
        return mark

三.两整数之和

不使用加减运算符计算两个整数的和。如果笔试题遇到直接return x+y

解法:

         其实两整数相加的本质就是其对应的二进制位相加,相加无非就是进位与不进位两种情况。对于a、b来说,它们对应相加的二进制位无非下面四种情况:

        a          b

————————————————

        0    +    1    =    1

        1    +    0    =    1

        0    +    0    =    0

        1    +    1    =    0(2,需要进位:10)

        我们之间了解过,异或运算符就是无进位相加的结果,所以我们可以先计算出a+b的无进位相加的结果,然后再加上进位即可。

        那么怎么计算进位的结果呢?我们观察二进制位相加的可能性,只有11才会进位,所以我们可以借助&运算,只要有0,就不会进位,结果都是0,如果都是1,就需要进位结果就是1.所以我们就可以借助a&b来得到进位。但是我们进位是要进到前一位,所以我们还需要让a&b的结果左移一位。

        得到进位之后,我们在让a^b无进位相加上进位(a&b)<<1,这样就将这两部分加在了一起。但是这样相加之后,可能有出现了需要进位的情况,所以我们依旧得先进行无进位相加,接着求进位,将这两部分相加后重复该操作。知道进位为0.

 a      b

13 + 23

——————————————第一次

        00001101

        00010111

a^b 

        00011010

(a&b)<<1

        00001010

——————————————第二次

a = 00011010

b = 00001010

a^b

        00010000

(a&b)<<1

        00010100

——————————————第三次

a = 00010000

b = 00010100

a^b        

        00000100

(a&b)<<1

        00100000

——————————————第四次

a = 00000100

b = 00100000

a^b

        00100100

(a&b)<<1

        00000000

——————————————此时进位为0,结束循环,返回a^b

a^b = 00100100 = 36

// C++

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

 四.只出现一次的数2

法一:

        我们可以借助哈希表来统计每个元素出现的次数,返回只出现了一次的元素即可

        时间复杂度:O(n),遍历一遍数组和一遍哈希表即可

        空间复杂度:O(k),k表示数组中不同元素的个数

法二: 

        我们可以依次求出最终结果的每一个二进制位的数是0还是1。具体的求答案的第i位二进制(i从0开始),对于非答案元素来说,它们都出现了三次,所以第i位的二进制要么是三个0,要么是三个1。所以对于所有的非答案元素来说,它们第i位的和一定是3的倍数。然后再加上答案第i位的二进制0/1,结果要么还是3的倍数,要么就是3的倍数+1.所以我们最后只要对第i位的和%3,就可以拿出答案的第i位的二进制。

        得到第i位之后,我们需要将第i位设置好,如果是0则不必,如果是1,则需要将第i位变成1 ret |= (1<<i)

        时间复杂度:O(32*n), 我们需要遍历求出每一个二进制位,求每一个二进制时还需要遍历数组。

        空间复杂度:O(1),只使用了一个变量存储最终结果

// C++

// 法一:
class Solution 
{
public:
    int singleNumber(vector<int>& nums) 
    {
        unordered_map<int,int> mp;
        for(auto e : nums) mp[e]++;
        for(auto e : mp)
            if(e.second == 1) return e.first;
        return 0; // 为了符合语法规范
    }
};

// 法二:
class Solution 
{
public:
    int singleNumber(vector<int>& nums) 
    {
        int ret = 0;
        for(int i=0; i<32; ++i)
        {
            int tmp = 0;
            for(auto e : nums)
            {
                tmp += (e>>i)&1;
            }
            if(tmp % 3) ret |= (1<<i);
        }
        return ret;
    }
};

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

1~n有n-1个数,数组中少了两个数,所以数组的大小加上2就是N。

这道题其实是之前两道题的结合版。数组中少个两个数,当我们求出N之后,将1~N和数组中的数结合其实,这道题不就变成了一个数组中,只有两个数出现了一次,其他都出现了两次,求这两个数。

解法:

        将数组和1~N这N个数都异或在一起,然后通过异或结果的最右侧的二进制是0还是1进行分类,重新进行异或,分类异或的结果就是答案

        时间复杂度:O(n),本质上我们就是遍历了4遍数组

        空间复杂度:O(1),只使用了两个变量来存储结果

// C++

class Solution 
{
public:
    vector<int> missingTwo(vector<int>& nums) 
    {
        // 求出N,然后转换成求一个数组所有的数都出现了两次,只有两个数出现了1次
        int n = nums.size()+2;
        int mark = 0;
        for(auto e : nums) mark ^= e;
        for(int i=1; i<=n; ++i) mark ^= i;
        int lao = mark & (-mark); // 找出最右侧的1

        int x1 = 0, x2 = 0;
        for(auto e : nums)
        {
            if(e & lao) x1 ^= e;
            else x2 ^= e;
        }
        for(int i=1; i<=n; ++i)
        {
            if(i & lao ) x1 ^= i;
            else x2 ^= i;
        }
        return {x1,x2};
    }
};

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

相关文章:

  • 自制虚拟机(C/C++)(三、做成标准GUI Windows软件,扩展指令集,直接支持img软盘)
  • 2.攻防世界PHP2及知识点
  • 【Vite + Vue + Ts 项目三个 tsconfig 文件】
  • 使用 DeepSeek-R1 等推理模型将 RAG 转换为 RAT,以实现更智能的 AI
  • 使用真实 Elasticsearch 进行高级集成测试
  • 《深入分析 TNN、MNN 和 NCNN:为不同硬件平台挑选最佳深度学习框架》
  • 【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.13 零拷贝技巧:as_strided的魔法与风险
  • 【Linux系统】信号:信号保存 / 信号处理、内核态 / 用户态、操作系统运行原理(中断)
  • 进程控制-下篇
  • cpp的STL与java的Collections Framework使用
  • 汇编知识点汇总
  • MVC、MVP和MVVM模式
  • 刷题记录 动态规划-3: 70. 爬楼梯
  • SpringMVC拦截器详解:原理、使用与配置
  • 【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.4 索引优化:避免意外复制的高效技巧
  • deepseek使用教程
  • 力扣 347. 前 K 个高频元素
  • Baklib赋能企业提升内容中台构建效率的全新路径解析
  • 基于人脸识别的课堂考勤系统
  • 【系统迁移】将系统迁移到新硬盘中(G15 5520)
  • 高清种子资源获取指南 | ✈️@seedlinkbot
  • 第六篇:事务与并发控制
  • 算法【混合背包】
  • 深入探索 Android 技术:从基础到前沿
  • 向上管理的必要性
  • 本地部署DeepSeek 多模态大模型Janus-Pro-7B