目录
常见算法总结:
1.1题目链接:371.两整数之和
1.2题目描述:
给你两个整数 a
和 b
,不使用 运算符 +
和 -
,计算并返回两整数之和。
示例 1:
输入:a = 1, b = 2 输出:3
示例 2:
输入:a = 2, b = 3 输出:5
提示:
-1000 <= a, b <= 1000
1.3解法(位运算):
1.3.1(比较蠢的解法,是我刚接触题目时立刻想到的)
算法思路:
直接遍历两个数的每一个bit位。无非就三种情况:
- 两数的第i个比特位都是0,那么两数相加第i位是0。
- 第i个比特位,一个数是1另一个数是0,两数相加第i位是1。
- 这是最难的一种情况:第i个比特位,两个数都是1。那么两数相加的第i比特位要进1,所以i+1的位置是1,i的位置变成0。但是我们需要考虑一个问题,如果i+1的位置是情况2呢?本身就是1,那么我们就必须一直往后进位。如果i+1的位置本身是1,那么就让i+1的位置为0,一直等到出现第1种情况的位置比特位为1就停止。
class Solution {
public:
int getSum(int a, int b) {
for (int i = 0; i < 32; i++)
{
if (((a >> i) & 1) == 1 && ((b >> i) & 1) == 1)
{
for(int j = i;j<32;j++)//确认进1的位置。
{
if (((a >> j) & 1) == 0)
{
a = a | (1 << j);
break;
}
a = a & (~(1 << j));
}
}
else if (((a >> i) & 1) == 0 && ((b >> i) & 1) == 1)
{
a = a | (1 << i);
}
}
return a;
}
};
1.3.2(比较聪明的算法)
算法思路:
- 异或^运算本质是无进位加法;(就是为了得到两数的第i位一个为0,一个为1,所以两数相加第i位为1)
- 按位与&操作能够得到进位;(就是为了两数第i位都是1,要对第i+1位进行进1)
- 然后一直循环进行,直到进位变成0为止。(之所以要一直循环,是因为对第i+1位置进1时,发现第i+1位就是1,所以一直循环找到某比特位为0要对其进行进1)
class Solution {
public:
int getSum(int a, int b) {
int num1 = a;
int num2 = b;
while(num2)
{
a = num1^num2;
b = (num1&num2)<<1;
num1 = a;
num2 = b;
}
return a;
}
};
2.1题目链接:137.只出现一个的数字||
2.2题目描述:
给你一个整数数组 nums
,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。
示例 1:
输入:nums = [2,2,3,2] 输出:3
示例 2:
输入:nums = [0,1,0,1,0,1,99] 输出:99
提示:
1 <= nums.length <= 3 * 104
-231 <= nums[i] <= 231 - 1
nums
中,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次
2.3解法(比特位计数):
算法思路:
设要找的数位ret。
由于整个数组中,需要找到元素只出现了一次,其余的数都出现了三次,因此我们可以根据所有数某一个比特位的总和%3的结果,快速定位到ret的一个比特位上的值是0还是1。
这样我们可以通过ret的每一位比特位的上的值,就可以将ret给还原出来。
(以此可知,无论其余数出现了n次,都可以用这方法找到只出现一次的数)
class Solution {
public:
int singleNumber(vector<int>& nums) {
int dog = 0;
for(int i = 0;i<32;i++)
{
int total = 0;
for(int& x : nums)//找到比特位%3==1的位置
{
total += ((x>>i)&1);
}
if(total%3 == 1)
{
dog |=(1<<i);
}
}
return dog;
}
};
3.1题目链接:面试题17.19.消失的两个数字
3.2题目描述:
给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?
以任意顺序返回这两个数字均可。
示例 1:
输入:[1]
输出:[2,3]
示例 2:
输入:[2,3]
输出:[1,4]
提示:
nums.length <= 30000
3.3解法(位运算):
我们可知,tmp = a^b。我们寻找tmp第一个为1的bit位为i。因为第i位为1,一定是a和b的第i位一个为1另一个为0。所以我们把nums数组和nums.size()+2数组整合起来,将第i位为1和第i位为0的数分开。两组间分别进行异或,最后就能找到消失的两个数。
class Solution
{
public:
vector<int> missingTwo(vector<int>& nums)
{
//1.将所有的数异或在一起
int tmp = 0;
for (auto x : nums)tmp ^= x;
for (int i = 1; i <= nums.size() + 2; i++)tmp ^= i;
//2.找出a,b中比特位不同的那一位
int diff = 0;
while (1)
{
if (((tmp >> diff) & 1) == 1)break;
else diff++;
}
//3.根据diff位的不同,将所有的数划分为两类来异或
int a = 0, b = 0;
for (int x : nums)
if (((x >> diff) & 1) == 1)b ^= x;
else a ^= x;
for (int i = 1; i < nums.size() + 2; i++)
if (((i >> diff) & 1) == 1) b ^= i;
else a ^= i;
}
};
可以给你们看看我一开始非常蠢的写法,直接用位图记录出现的数字,主要是一开始他说对时间复杂度有要求,我就只好用这种拿空间换时间的方法
class Solution {
public:
vector<int> missingTwo(vector<int>& nums) {
int arr[3200] = { 0 };
vector<int> ret;
for (int& x : nums)
{
int i = x / 32;
int j = x % 32;
arr[i] |= 1 << j;
}
arr[0] |= 1 << 0;
int count = 0;
for (int i = 0; i < 3200; i++)
{
for (int j = 0; j < 32 && (i * 32 + j <= nums.size() + 2); j++)
{
if (((arr[i] >> j) & 1) == 0)
{
ret.push_back(i * 32 + j);
count++;
}
}
if (count == 2)
break;
}
return ret;
}
};