与运算
- 与运算常用于二进制的取位操作,其用符号
&
表示,相同位的两个数字都为1,则为1,若有一个不为1,则为0
00101 & 11100 = 00100
3(11) & 2(10) = 2(10)
- 应用:任意一个数
&1
的结果就是取二进制的最末位,常用于判断数的奇偶,最末位为0表示该数为偶数,最末位为1表示该数为奇数(其他位都为2的正幂次方)
或运算
- 或运算常用于二进制特定位上的无条件赋值,其用符号
|
表示,相同位的两个数字只要一个为1即为1
00101 | 11100 = 11101
3(11) | 2(10) = 3(11)
- 应用:一个数
|1
的结果就是把二进制最末位强行变成1,如果需要把二进制最末位变为0,对这个数|1
后再减1即可,其实际意义就是把这个数强行变成最接近的偶数
非运算
- 非运算是把内存中的0和1全部取反,用符号
~
表示,使用非运算时要格外小心,需要注意整数类型有没有符号,如果非运算的对象是无符号整数,那么得到的值是它与该类型上界的差
异或运算
- 异或运算,是对等长二进制模式按位执行逻辑按位异或操作,用符号
^
表示,如果某位不同则该位为1,否则该位为0
00101^11100=11001
3(11) ^ 2(10) = 1(01)
- 异或运算的逆运算是它本身,也就是说两次异或同一个数最后结果不变,即:
(a^b)^b=a
左移运算
- 左移运算,用符号
<<
表示,操作a<<b
就是把a转为二进制后左移b位(在后面填充b个0),实质上就是a乘以2的b次方,因为在二进制数后面添个0就等于把这个数扩大2倍
400(110010000)<<2=100(1100100)
- 通常认为,
a<<1
比a*2
要更快,因为前者是更底层的操作,因此程序中乘以2的多少次方的操作尽量用左移位来代替
右移运算
- 右移运算,用符号
>>
表示,操作a>>b
就是把a转为二进制后右移b位(去掉末尾b个0),实质上就是a除以2的b次方(取整)
100(1100100)>>2=400(110010000)
- 通常认为,
a>>1
比a/2
要更快,因为前者是更底层的操作,因此程序中除以2的多少次方的操作尽量用右移位来代替
首先了解一下&&
的特性,比如有A&&B
- 如果
A为true
,返回B的布尔值(继续往下执行) - 如果
A为false
,直接返回false
(相当于短路)
求 1+2+…+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
示例 1:
输入: n = 3 输出: 6
示例 2:
输入: n = 9 输出: 45
限制:
1 <= n <= 10000
//以n=3为例
#include <iostream>
using namespace std;
int sumNums(int n);
int main()
{
cout << sumNums(3) << endl;
return 0;
}
int sumNums(int n)
{
n && (n += sumNums(n - 1));
cout << n << endl;
return n;
}
//
0
1
3
6
6
注意运算符的优先级防止出错
- 常用的运算符优先级
Example1
给定一个整数,编写一个函数来判断它是否是 2 的幂次方。
示例 1:
输入: 1
输出: true
解释: 20 = 1
示例 2:
输入: 16
输出: true
解释: 24 = 16
示例 3:
输入: 218
输出: false
解题思路
- 观察一些是2的幂的二进制数:
- 可以发现这些数,都是最高位为1,其他位为0.所以把问题转化为“判断一个数的二进制,除了最高位为1,是否还有别的1存在”
- 观察第一组数对应减1,再对两组数据求
&
运算
- 对于N为2的幂的数,都有
N & (N - 1) = 0
,所以这就是判断的
//c++
class Solution {
public:
bool isPowerOfTwo(int n) {
return n > 0 && ((n & (n - 1)) == 0);
}
};
Example2
示例 1:
输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串00000000000000000000000000001011 中,共有三位为 ‘1’。
示例 2:
输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 ‘1’。
示例 3:
输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串11111111111111111111111111111101 中,共有 31 位为 ‘1’。
解题思路
- 利用位运算的思想,构造出一个掩码来与目标值的当前位进行
&
运算 - 1的二进制
- 只需要让这个掩码每次向左移动一位,然后与目标值求
&
,就可以判断目标值的当前位是不是1,比如21的二进制
- 每次移动掩码来和当前位进行计算
Java实现
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int result = 0;
int mask = 1;
for(int i = 0;i < 32;i++)
{
if((mask & n) != 0)
result++;
mask <<= 1;
}
return result;
}
}
//左移mask会改变
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int result = 0;
int mask = 1;
for(int i = 0;i < 32;i++)
{
if((mask & n) == 1)
result++;
n >>= 1;
}
return result;
}
}
//右移n
- 优化:刚才对于判断2的幂数利用
n & (n - 1)
方式,去掉n
的最后一个1
Cpp实现
class Solution {
public:
int hammingWeight(uint32_t n) {
int count = 0;
while(n > 0)
{
n &= (n - 1);
count++;
}
return count;
}
};
Example3
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
解题思路
- 利用异或运算:通过异或运算的结合律,出现两次的元素异或后为0,再根据0和0的异或仍然为0,在去跟出现一次的数字作异或运算这样就得到只出现一次的数字了,在这里我们按照数组的顺序来进行异或运算即可
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res = nums[0];
for(int i = 1;i < nums.size();i++)
{
res ^= nums[i];
}
return res;
}
};
Example3
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,3,2]
输出: 3
示例 2:
输入: [0,1,0,1,0,1,99]
输出: 99
解题思路
- [A,A,A,B,B,B,C,C,C]和[A,A,A,B,B,B,C]差了两个C即:3×(a+b+c) - (a+a+a+b+b+b+c) = 2c,也就是说,如果把原数组去重、再乘以3得到的值,刚好就是要找的元素的两倍
Python实现
注:使用了额外的空间
class Solution:
def singleNumber(self, nums: List[int]) -> int:
return int((sum(set(nums)) * 3 - sum(nums)) / 2)
解题思路
- 位运算:让出现三次的元素归0(注意出现两次的元素通过异或运算可以归0)
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res = 0;
for (int i = 0; i < 32; ++i) {
int number = 0;
for (auto x : nums) {
number += (x>>i)&1;
}
res |= (number % 3)<<i;
}
return res;
}
};
Example4
给定一个包含 0, 1, 2, …, n 中 n 个数的序列,找出 0 … n 中没有出现在序列中的那个数。
示例 1:
输入: [3,0,1]
输出: 2
示例 2:
输入: [9,6,4,2,3,5,7,0,1]
输出: 8
解题思路
- 利用高斯公式:首项加末项的和乘以项数除以2先确定0到n的和,之后减去给定数组的所有元素之和,即得到缺失的元素
Cpp实现
class Solution {
public:
int missingNumber(vector<int>& nums) {
int l = nums.size();
int s = l * (l + 1) / 2;
int sum = 0;
for(int i = 0;i < l;i++)
{
sum += nums[i];
}
return s - sum;
}
};
解题思路
- 利用异或位运算
Java实现
class Solution {
public int missingNumber(int[] nums) {
int res = 0;
for(int i = 0;i < nums.length;i++)
{
res ^= nums[i] ^ i;
}
return res ^ nums.length;
}
}