leetcode 136 只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
解题思路:
异或的计算性质:2 ^ 3 ^ 2 ^ 4 ^ 4 等价于 2 ^ 2 ^ 4 ^ 4 ^ 3 => 0 ^ 0 ^3 => 3
所以我们将所有数进行异或,最后剩余的那个就是我们要找的那个数。注意result初始为0。
class Solution {
public:
int singleNumber(vector<int>& nums) {
int len = nums.size();
int result=0;
for(int i=0;i<len;i++){
result ^=nums[i];
}
return result;
}
};
由这道题衍生出很多关于位运算的题目,这里先介绍一下异或运算:
相同的为0,不同的为1
异或运算的一些性质:
1、交换律
2、结合律(即(a^b)^c == a^(b^c))
3、对于任何数x,都有x^x=0,x^0=x
运用异或运算可以巧妙的解决一些位运算的问题,大大地提高了计算效率。
leetcode 461 汉明距离
两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。
给出两个整数 x
和 y
,计算它们之间的汉明距离。
注意:
0 ≤ x
, y
< 231.
示例:
输入: x = 1, y = 4
输出: 2
解释:
1 (0 0 0 1)
4 (0 1 0 0)
↑ ↑
思路:首先将给出的两组数合并为一组,通过异或运算可以保留数字不同的索引位置。
然后通过 z&(z-1) 每次循环可以去除掉最低位的1,同时计算1的数量。
class Solution {
public:
int hammingDistance(int x, int y) {
int z = x ^ y;
int cnt = 0;
while (z > 0) {
cnt++;
z = z & (z - 1);
}
return cnt;
}
};
leetcode 476 数字的补数
给定一个正整数,输出它的补数。补数是对该数的二进制表示取反。
注意:
- 给定的整数保证在32位带符号整数的范围内。
- 你可以假定二进制数不包含前导零位。
示例 1:
输入: 5
输出: 2
解释: 5的二进制表示为101(没有前导零位),其补数为010。所以你需要输出2。
示例 2:
输入: 1
输出: 0
解释: 1的二进制表示为1(没有前导零位),其补数为0。所以你需要输出0。
通过辗转相除法计算十进制数对应的二进制,同时取反,然后再计算出二进制对应的十进制数。
class Solution {
public:
int findComplement(int num) {
int res=0;
stack<int> s;
while(num>0){
if(num%2==1){
s.push(0);
}
else{
s.push(1);
}
num/=2;
}
int i=s.size()-1;
while(s.size()){
res+=s.top()*pow(2,i);//101=1*2^2+0*2^1+1*2^0;
s.pop();i--;
}
return res;
}
};
leetcode 169 求众数
给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋
的元素。
你可以假设数组是非空的,并且给定的数组总是存在众数。
示例 1:
输入: [3,2,3]
输出: 3
示例 2:
输入: [2,2,1,1,1,2,2]
输出: 2
解题思路:从第一个数开始num=1,遇到相同的就加1,遇到不同的就减1,减到0就重新换个数开始计数,总能找到最多的那个
class Solution {
public:
int majorityElement(vector<int>& nums) {
int num=1;int res=nums[0];
for(int i=1;i<nums.size();i++){
if(num!=0){
if(res==nums[i]){
num++;
}
else{
num--;
}
}
else{
res=nums[i];num=1;
}
}
return res;
}
};
leetcode 389 找不同
给定两个字符串 s 和 t,它们只包含小写字母。
字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母。
请找出在 t 中被添加的字母。
示例:
输入:
s = "abcd"
t = "abcde"
输出:
e
解释:
'e' 是那个被添加的字母。
解题思路:运用异或的性质,将两个字符串中的字符依次异或,最终得到不同的那个,注意ans初始化为空字符。
class Solution {
public:
char findTheDifference(string s, string t) {
//找出一个不同的,几乎都是异或
char ans = ' ';//ASCII码为0
for(int i = 0;i < s.size();++i)
{
ans ^= s[i];
ans ^= t[i];
}
return ans ^= t[t.size() - 1] - 32;
}
};
leetcode 268 缺失数字
给定一个包含 0, 1, 2, ..., n
中 n 个数的序列,找出 0 .. n 中没有出现在序列中的那个数。
示例 1:
输入: [3,0,1]
输出: 2
示例 2:
输入: [9,6,4,2,3,5,7,0,1]
输出: 8
解题思路:巧妙利用数组索引与数组中的元素进行异或,最终得到缺失的数字,这里注意res的初始值为数组的长度,这样保证了从0到n一共n+1个数与数组中的n个数进行异或。
class Solution {
public:
//异或
int missingNumber(vector<int>& nums) {
int res=nums.size();
for(int i=0;i<nums.size();i++){
res^=nums[i];
res^=i;
}
return res;
}
};
leetcode 371 两整数之和(剑指offer65)
不使用运算符 +
和 -
,计算两整数 a
、b
之和。
示例 1:
输入: a = 1, b = 2
输出: 3
示例 2:
输入: a = -2, b = 3
输出: 1
解题思路:通过拆解加法步骤来观察计算细则
1.通过异或操作计算出不进位加法每一位的值
2.通过与操作和左移运算得出进位值
3.将1和2进行相加得到最终值
class Solution {
public:
int getSum(int a, int b) {
while(b)
{
// 防止 AddressSanitizer 对有符号左移的溢出保护处理
auto c = ((unsigned int) a & b) << 1;//求两数相加的进位,与运算和左移运算,得相加之后进位所在位得值
a = a ^ b; //异或操作:不进位加法
b = c;
}
return a;
}
};
leetcode 191 位1的个数
编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。
示例1:
输入:00000000000000000000000000001011 输出:3 解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'
示例 2:
输入:00000000000000000000000010000000 输出:1 解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。
示例 3:
输入:11111111111111111111111111111101 输出:31 解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。
解题思路:通过不断的与1做与运算,这样可以判断最后一位是否为1,如果是1,则and加1.
class Solution {
public:
int hammingWeight(uint32_t n) {
int ans=0;
while(n)
{
ans+=n&1;
n>>=1;
}
return ans;
}
};
leetcode 190 颠倒二进制位
颠倒给定的 32 位无符号整数的二进制位。
示例 1:
输入: 00000010100101000001111010011100
输出: 00111001011110000010100101000000
解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596,
因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。
示例 2:
输入:11111111111111111111111111111101
输出:10111111111111111111111111111111
解释:输入的二进制串 11111111111111111111111111111101 表示无符号整数 4294967293,
因此返回 3221225471 其二进制表示形式为 10101111110010110010011101101001。
解题思路:理解二进制的本质,ans加上提取出的最后一位,再左移。相反,原本的输入右移。
class Solution {
public:
uint32_t reverseBits(uint32_t n) {
uint32_t ans=0;
//进制的本质
int i=32;
while(i--)
{
ans<<=1;
ans+=n&1;
n>>=1;
}
return ans;
}
};