赞
踩
<< : 二进制位整体左移
>> : 二进制位整体右移
~ : 按位取反
& : 按位与
| : 按位或
^ : 按位异或 (无进位相加)
(n >> x) & 1
(1<<x) & n
(~(1<<x)) & n
位图就是一种利用比特位去承载信息的一种哈希思想,通常可以用来记录“在不在”等问题,利用32位比特位的一位或者多位去记录某种信息状态,和哈希表利用数组是一样的,位图是解决一些大基数的简单问题,具体操作这里不做详细整理
n&(-n)
解释:因为-n就是将n的二进制先按位取反,然后再+1,例如:
原码:01100100
补码:10011011
反码:10011100
我们会发现将n变成-n之后,最右侧的1往后位置都保持不变,而1前面的位置的都相反,这是由于取反后又加1,前面部分不同因为取了反,后面因为加1进位,使得最右侧的1和原先的保持,所以n&(-n)可以提取最后一位1
n&(n-1)
这个太常见了,因为最后减1会相最右侧的1借位,所以n-1最右侧的1以后都会不同,而不影响前面
管它什么优先级,按自己的思路加括号!!!
a^b^c = a^(b^c) ,即异或操作不在乎顺序
利用该性质有一个经典且量身定做的题目:只出现一次的数字 以及其变形 只出现一次的数字|||
因为太经典了,一搜就搜到了,简单提供下思路,只出现一次的数字,就是有一个数只有一个,其他都是成对出现的,因此只需要拿一个0去将这组数全部异或,最终结果就是那个只出现一次的数
而只出现一次的数字|||相对需要做一些处理,题目大致和原先一样,不过有两个只出现了一次的数字,其余都是出现两次的相同数,这个时候需要先将这组数据进行处理,还是利用异或去将这组数据进行分组,假如不同的那两个数是a和b,将a和b异或后,一定会有一个不等于0的数,我们拿到这个数最右侧的1位置去将数据分成两组数据,相同数据一定会被分到一组中,而那两个不同的数会被分到不同组中,此时问题就变回原先的模型了
面试题 01.01. 判定字符是否唯一 - 力扣(LeetCode)
该题目若是不考虑额外的限制条件,则只需要使用哈希表,遍历字符串的同时若是遇到已经存在的字符则返回false,若是没有则存入字符到哈希表中,最终遍历结束后都没有重复则返回true
优化:题目要求若是不用额外的数据结构在面试中会很加分,我们可以使用位图的思想去完成,因为题目中提到只有小写字母,因此只有26种字符,所以直接用一个整形中的32bit位即可完成,思路上和哈希统计是没有区别的,只不过在使用位图的时候,更多的是通过位运算的操作,例如如果将某位变成1等等
- class Solution {
- public:
- bool isUnique(string astr)
- {
- if(astr.size() > 26) return false;
- int hash = 0;
- for(int i = 0;i<astr.size();i++)
- {
- int x = astr[i] - 'a';
- if( ( ( hash>>x )&1 ) == 0 )
- hash |= 1<<x;
- else
- return false;
- }
- return true;
- }
- };
这题的方法很多:哈希、二分法、数学方法、位运算
可以使用哈希表的方式去统计
也可以采用二分法,这和之前做过的一题点名很像
数学方法则是高斯求和,先将0到n的和算出来,然后减去数组和即可
位运算则是用异或的性质,将数组中所有数进行异或,然后再和0到n的数异或
这里只提供一个位运算的代码参考,其他的都挺简单,可以自己尝试去写写
- class Solution {
- public:
- int missingNumber(vector<int>& nums)
- {
- int n = 0;
- for(auto& x:nums) n^=x;
- for(int i = 0;i<=nums.size();i++) n^=i;
- return n;
- }
- };
两数之和我们利用位运算中的按位异或,按位异或可以被看作为无进位相加,也就是说,只要处理好进位的问题,就可以实现相加,而进位可以通过按位与去得到那些部分需要进位
- class Solution
- {
- public:
- int getSum(int a, int b)
- {
- int ret = a^b;
- unsigned int up = a&b;
- while(up)
- {
- int tmp = ret^(up<<1);
- up = ret&(up<<1);
- ret = tmp;
- }
- return ret;
- }
- };
137. 只出现一次的数字 II - 力扣(LeetCode)
- class Solution {
- public:
- int singleNumber(vector<int>& nums)
- {
- int ret = 0;
- for(int i = 0;i<32;i++)
- {
- int bit = 0;
- for(auto x : nums)
- {
- bit +=((x>>i)&1);
- }
- bit%=3;
- if(bit == 1) ret |= (1<<i);
- }
- return ret;
- }
- };
面试题 17.19. 消失的两个数字 - 力扣(LeetCode)
这题的思路我们可以认为,是《消失的数字》+《只出现一次的数字|||》两个题目的合并,我们只需用一个0将【1,N】的数字都异或一次,再将数组里的数异或一次,就可以得到那消失的两个数的异或,再通过这个异或值对(【1,N】的数+数组的数)进行分组处理,分别用一个数去异或两组不同的数,最终得到的两个数就是消失的两个数
- class Solution {
- public:
- vector<int> missingTwo(vector<int>& nums)
- {
- int n = nums.size();
- int tmp = 0;
- for(auto x : nums) tmp^=x;
- for(int i = 1;i<=n+2;i++) tmp^=i;
- int a = 0;
- int b = 0;
- for(int i = 1;i<=n+2;i++)
- {
- if(i&(tmp&(-tmp)))
- a^=i;
- else
- b^=i;
- }
- for(auto x:nums)
- {
- if(x&(tmp&(-tmp)))
- a^=x;
- else
- b^=x;
- }
- return {a,b};
- }
- };
本篇章总结了关于位运算的相关经典算法,其中有几道面试常见题,虽然简单,但思路巧妙
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。