算法:图解位运算以及鸽巢原理应用

这篇具有很好参考价值的文章主要介绍了算法:图解位运算以及鸽巢原理应用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本篇总结位运算中常见的算法题和思路,首先总结位运算中常见的题型

实现原理

基础位运算

位运算主要包含

  1. 左移 <<
  2. 右移 >>
  3. 按位取反 ~
  4. 按位与 &
  5. 按位或 |
  6. 按位异或 ^

位图思想

1. 给定一个数n,确认它的二进制表示中第x位是0还是1

解法:(n>>x) & 1
原理:n右移x个单位,就令所求元素的二进制位移动到了第一位,再令其和1按位与,其他位都是0,只有第一位,如果n的这个位置为1,则结果为1,如果是0,则结果为0

2. 给定一个数n,将它的二进制表示的第x位改成1

解法:(1<<x) | n
原理:将1左移x个单位,就可以让二进制表示中1挪动到x的位置,再让这个左移后的1和n按位或,则就可以改变这个位置的0

3. 将一个数n的二进制表示的第x位修改成0

解法:(~(1<<x)) & n
原理:将1左移x个单位,此时除了第x位置外,其余位置都为0,再令其按位取反,此时除了第x个位置外,其余地方都为1,再让这个数和n按位与,此时其余位置不变,第x的位置就会被改变为0

找最右侧数

1. 提取一个数n二进制中最右侧的1

解法:n & -n
原理:-n的原理是按位取反再+1,将最右侧的1,左边的区域全部取反就是-n

以下图例子为例:

算法:图解位运算以及鸽巢原理应用,C++,# 算法,习题集,算法

2. 去掉一个数n二进制表示中最右侧的1

解法:n & (n-1)
原理:n-1就可以把最后一个1右侧的部分全部取反,此时按位与即可

按位异或

主要应用场景是单身狗等问题

算法思路

算法思路主要就是前面的这些原理,而大部分题目就是基于上面的原理进行一些叠加,只需要把题目进行一定程度的拆分,就可以解题了

典型例题

基础位运算

位运算在前面已经学过一些,这里主要总结一些比较常见的,比较有典型的例子,较简单的不归纳

只出现一次的数字

算法:图解位运算以及鸽巢原理应用,C++,# 算法,习题集,算法

class Solution 
{
public:
    int singleNumber(vector<int>& nums) 
    {
        int ret=0;
        for(auto ch : nums)
        {
            ret^=ch;
        }
        return ret;
    }
};

这里主要就是用到了按位异或的性质,按位异或的一个重要性质就是,如果一组数中每一个元素出现的此时都是偶数,只有一个是奇数,那么只需要把这个数组中所有的数据都按位异或起来,最终剩下的数就是那个数,这是由于按位异或自身的原理所产出的算法原理

只出现一次的数字III

算法:图解位运算以及鸽巢原理应用,C++,# 算法,习题集,算法

class Solution 
{
public:
    vector<int> singleNumber(vector<int>& nums) 
    {
        int ret=0;
        for(auto ch :nums)
        {
            ret=ret^ch;
        }
        int lsb= ret==INT_MIN? ret:(ret&(-ret));
        int type1=0,type2=0;
        for(auto num:nums)
        {
            if(num &lsb)
            {
                type1 ^=num;
            }
            else
            {
                type2 ^=num;
            }
        }
        return {type1,type2};
    }
};

这个题本身就是基于上面题目的原理产生的,按位异或可以找到一组数中唯一出现的数,那么在这个题中却有两个数都唯一出现的,那么首先要做的就是进行分类,让这两个数归类到两个类中,以此能让其分开

那么分类的原理就是让所有数都按位异或,最终得到的就是这两个数按位异或的结果,那现在要做的就是要找到这两个数的不同点,然后把这两个数分开,具体方法就是用到了前面总结的找到不一样的1,令ret&(-ret),这样就可以找到二进制中最右侧的1,而这个1就是区分这两组数据的唯一标准

那下来做的就是把nums中的元素都和前面ret&(-ret)的结果按位异或,由于最右侧1的不同,就会天然的把数组里面的数据分成两组,而这两个不同的数据也会被分到两组中,这样找到了唯二的数据

经典题型

判断字符是否唯一

算法:图解位运算以及鸽巢原理应用,C++,# 算法,习题集,算法

这里解决方法很多,可以用哈希表,排序,等等,这里采用是位图的思想,同时利用鸽巢原理进行一定程度的优化

class Solution 
{
public:
    bool isUnique(string astr) 
    {
        if(astr.size()>26)
        {
            return false;
        }

        int bitmap=0;
        for(auto ch : astr)
        {
            if((bitmap>>(ch-'a'))&1==1)
            {
                return false;
            }
            else
            {
                bitmap=bitmap | (1<<(ch-'a'));
            }
        }
        return true;
    }
};

两整数之和

算法:图解位运算以及鸽巢原理应用,C++,# 算法,习题集,算法

此题需要用到按位与和按位异或的性质,按位与的性质除了相同为0,相异为1外,还有一个性质是无进位相加,因此可以利用这个性质解题,先用按位异或进行无进位相加,再求出进位是多少,再继续循环相加即可

而进位其实就是直接用的性质是两个数的二进制位都为1,则要进1,如果有一个为0则为0,其实这就是按位与的操作,但是由于进位要进1,因此这里把运算出的结果左移一个单位其实就是最终的结果,那么依据这个原理就可以求出最终的结果,当进位为0的时候循环结束

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

算法:图解位运算以及鸽巢原理应用,C++,# 算法,习题集,算法

对于这个题首先可以采用哈希表的方法解决,但更好的方法是使用位运算,需要一定的观察

这里采用的是比特位计数的方法,原理就是通过观察这些数,找到每一个单独的比特位拥有的独特的规律,那对于这个题来说,规律就是对于数组nums,它当中每一个数的某一个比特位相加,最终相加的结果%3得到的就是这唯独一个数据的比特位的值,下图来解释

算法:图解位运算以及鸽巢原理应用,C++,# 算法,习题集,算法

这里是举了具体的例子,如果把它抽象成字母来代表其中的数据,其实也是一样的,因为数字要不然是三个一组,要不然一个一组,而三个一组的数据最终都能被三整除,最后唯独不同的就是一个一组的数据

利用这个原理,就能很轻松的把原来的ret的每一个比特位都得到,得到每一个比特位最终这个数就是我们要求的数据

class Solution 
{
public:
    int singleNumber(vector<int>& nums) 
    {
        int ret=0;
        for(int i=0;i<32;i++)
        {
            int sum=0;
            for(auto ch:nums)
            {
                sum+=(ch>>i) & 1;
            }
            sum%=3;
            ret = ret | (sum<<i);
        }
        return ret;
    }
};

消失的两个数字

算法:图解位运算以及鸽巢原理应用,C++,# 算法,习题集,算法

本题实现原理其实就是前面两个题的结合,分别是消失的数字只出现一次的数字III,但这个题的解法还有很多种,其中一种是借助鸽巢原理进行排序解决,也是一种思维较为巧妙的一种解法,可以借鉴学习

下面展示的是位运算的传统解法,鸽巢原理后续进行补充

class Solution 
{
public:
    vector<int> missingTwo(vector<int>& nums) 
    {
        int n=nums.size();
        // 找到两个数按位异或的结果
        int ret=0;
        for(int i=1;i<=n+2;i++)
        {
            ret ^= i;
        }
        for(auto ch : nums)
        {
            ret ^= ch;
        }
        // ret就是两个数按位异或的结果
        // 现在把这两个数分开找到即
        int lsb=ret&(-ret);    // 找到不同点
        int type1=0,type2=0;
        for(int i=1;i<=n+2;i++)
        {
            if(i & lsb)
            {
                type1 ^= i;
            }
            else
            {
                type2 ^= i;
            }
        }
        for(auto p : nums)
        {
            if(p & lsb)
            {
                type1 ^= p;
            }
            else
            {
                type2 ^= p;
            }
        }
        return {type1 , type2};
    }
};

鸽巢原理

鸽巢原理的概念就是,如果有n+1个鸽子飞进了n个鸽巢中,那么必定有鸽巢中至少飞进了2只鸽子

其实在前面的题目中,也有部分题可以使用鸽巢原理进行解决,例如在哈希表的使用中就经常可以用到鸽巢原理进行一定程度的优化,例如判断一个字符串中每个字符只出现一次,那么此时,如果字符串的长度大于26,那么说明这里必定会不是我们所要找的

类似的题目还有很多,但是对于上面的题目一个很简单的方法就是借助鸽巢原理进行排序来解决题目

依据这个原理,可以实现一个时间复杂度只有O(N)的排序,但是这个排序有一定的条件,条件就是要排序的数组必须是从1到n中所有数,并且每个数只出现一次,依据这个原理就可以解决问题

void sort(vector<int>& nums)
{  //6,1,2,5,-1,-1
	for (int i = 0; i < nums.size(); i++) 
	{
		while (nums[i] != -1 && nums[i] != i + 1)
		{
			swap(nums[i], nums[nums[i] - 1]);
		}
	}
}

int main()
{
	vector<int> v{ 6,1,2,5,-1,-1 };
	sort(v);
	return 0;
}

总结

位运算在面试的场景中有考察,也算是一种比较重要的算法,是应该多多练习的,而在练习的过程中要清楚位运算的基本解题原理,掌握了基本解题原理很多题目就是在基本的解题原理上进行的延伸,那么此时再进行解题就简单很多了文章来源地址https://www.toymoban.com/news/detail-690496.html

到了这里,关于算法:图解位运算以及鸽巢原理应用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 网络安全习题集

    4 ISO / OSI 安全体系结构中的对象认证安全服务使用( C ) 机制来完成。 A .访问控制 B .加密 C .数字签名 D .数据完整性 5 身份鉴别是安全服务中的重要一环,以下关于身份鉴别的叙述不正确的是( B ) A .身份鉴别是授权控制的基础 B .身份鉴别一般不用提供双向认证 C .目

    2024年02月19日
    浏览(35)
  • 数据结构习题集

    目录 第一章 绪论 一 选择题。  二 填空题。 第二章.线性表 一 选择题。 二 填空题。 第三章.栈、队列 一 选择题。 二 填空题。 第六章.树与二叉树 一 选择题。 二 填空题。 三 简答题。 第七章.图 一 选择题。 二 填空题。 三 简答题。 第九章.查找 一 选择题。 二

    2024年02月07日
    浏览(36)
  • javaweb习题集

    1.下列关于静态网页和动态网页说法错误的是C A、动态网页的内容一般是从数据库里面读取出来的 B、动态网页的内容的显示是通过程序来实现的 C、动态网页上可以显示动态元素比如:动画,视频等,而静态网页无法显示动态元素 解析:动态网页,与网页上的各种动画、滚动

    2024年02月05日
    浏览(41)
  • 《网络安全基础》——习题集

    一、 选择题: 1、TCP/IP 体系结构中的TCP 和IP 所提供的服务分别为() A.链路层服务和网络层服务 B.网络层服务和传输层服务 C.传输层服务和应用层服务 D.传输层服务和网络层服务 2、下列哪个攻击不在网络层() A.IP 欺诈 B. Teardrop C. Smurf  D. SQL 注入 3、ARP 协议是将 __ 地址转换成

    2024年02月08日
    浏览(30)
  • leetcode习题集【8月】

    617. 合并二叉树 700. 二叉搜索树中的搜索 236. 二叉树的最近公共祖先 701. 二叉搜索树中的插入操作 108. 将有序数组转换为二叉搜索树 538. 把二叉搜索树转换为累加树

    2024年02月11日
    浏览(38)
  • 计算机网络习题集

    一、 单项选择题 计算机网络最突出的优点是( D ) A. 精度高 B. 内存容量大 C. 运算速度快 D. 共享资源 2.( D )不属于局域网的特点。 A.较小的地域范围 B.高传输速率和低误码率 C.一般为一个单位所建 D.一般侧重共享位置准确无误及传输的安全 3.网络协议主要要素为( C ) A、数据格

    2024年02月02日
    浏览(46)
  • PTA SQL部分练习题集

    10-1 找出所有姓“李”的学生姓名、民族和联系电话。 10-2 查询选修了2门以上课程的学生学号和平均成绩。 10-3 统计每种商品的销售数量 10-4 查询前3门课程的课号及课程名称 10-5 查询名字中含有“明”字的男生的学生姓名和班级 10-6 查询姓名是两个字的学生信息 10-7 计算“

    2023年04月14日
    浏览(69)
  • 【LeetCode】练习习题集【4月 - 7 月】

    1.重复数 题目: 代码: 9.回文数 题目: 思路: 如果是负数一定不是回文数 直接返回false 如果是正数,则将其倒序数值计算出来,然后比较和原数值是否相等 如果是回文数相等返回true 不相等返回false 代码: 13. 罗马数字转整数 (https://leetcode.cn/problems/roman-to-integer/) 题目:

    2024年02月13日
    浏览(31)
  • 信息学竞赛中的数学 习题集 461-470(10题)

    3279:【例46.1】 完全数 信息学奥赛一本通-编程启蒙(C++版)在线评测系统 3280:【例46.2】 数字统计 信息学奥赛一本通-编程启蒙(C++版)在线评测系统 3281:【例46.3】 素数回文数的个数 信息学奥赛一本通-编程启蒙(C++版)在线评测系统 3282:练46.1 求π的值 信息学奥赛一本

    2024年01月23日
    浏览(34)
  • 【Python习题集4】字符串与正则表达式

    1.输人一个字符串,将该字符串中下标为偶数的字符组成新串并通过字符串格式化方式显示。 (1)源代码 (2)运行结果截图 2.编写程序,生成一个由15个不重复的大小写字母组成的列表。 (1)源代码 (2)运行结果截图 3.给定字符串\\\"site sea suede sweet see kase sse sseeloses\\\",匹配出所有以

    2024年02月02日
    浏览(74)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包