
目录
LCR 133. 位 1 的个数
解法一:按位与
class Solution {
public:
int hammingWeight(uint32_t n) {
int count = 0;
for (int i = 0; i < 32; i++)
if (n & (1 << i)) count++;
return count;
}
};
方法二:统计 n &= n - 1
的运算次数可以求出整数二进制表示中1的个数。
class Solution {
public:
int hammingWeight(uint32_t n) {
int count = 0;
while (n)
{
n &= n - 1;
count++;
}
return count;
}
};
只出现一次的数字
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ret = 0;
for (auto e : nums) ret ^= e;
return ret;
}
};
LCR 004. 只出现一次的数字 II
数组nums中都是int类型的数,有一个数只出现一次,其他的都出现了三次,如果将数组中所有的数的某一个比特位上的值加起来,再模以3,那么得到的0或1就是只出现了一次的数在这个比特位上的值。那么将所有的数对应的32个比特位上的0或1加起来模3,我们就能得到那个只出现了一次的数的2进制数。
只出现了一次的数的第 i 个二进制位就是数组中所有元素的第 i 个二进制位之和除以 3 的余数。
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ret = 0;
for (int i = 0; i < 32; i++)
{
int tmp = 0;
for (auto e : nums) tmp += (e >> i) & 1;
if (tmp % 3) ret |= 1 << i;
}
return ret;
}
};
只出现一次的数字 III
思路一: 第一个思路是利用映射计数来找出只出现一次的数字,首先找出最大值和最小值,用相减的值来确定用来计数的数组开多大(注意还要+1),接着遍历原数据映射,最后遍历计数数组找出只出现一次的数字。
但是这个方法只能通过少数测试用例,当数据非常不集中时空间消耗太大。
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
int max = 0;
int min = 0;
for (int i = 1; i < nums.size(); i++)
{
if (nums[i] > nums[max])
{
max = i;
}
if (nums[i] < nums[min])
{
min = i;
}
}
int n = nums[max] - nums[min];
vector<int> count(n + 1, 0);
for (auto e : nums)
{
count[e - nums[min]]++;
}
vector<int> v;
for (int i = 0; i < n + 1; i++)
{
if (1 == count[i])
{
v.push_back(i + nums[min]);
}
}
return v;
}
};
思路二: 类似双指针遍历数据,得到只出现一个的数字的下标。
这个方法可以通过,但是时间复杂度是O(N^2),效率低。
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
vector<int> v;
for (int i = 0; i < nums.size(); i++)
{
int flag = 1;
for (int j = 0; j < nums.size(); j++)
{
if (i != j && nums[i] == nums[j])
{
flag = 0;
}
}
if (1 == flag)
{
v.push_back(nums[i]);
}
}
return v;
}
};
思路三: 位运算,使用异或操作符遍历数据,得到只出现一次的两个数m和n的异或值(为了防止溢出可以用unsigned int
接收异或值)。因为m和n不相等,所以这个异或值必然有个比特位上的值是1。 m和n在这个比特位上一个是0,一个是1。
接下来我们拿着这个比特位上的1再次使用异或操作符遍历数据,对于这个比特位,可以把值为1的分到一组,把值为0的分到一组,那么m和n必然被分到不同的组中,这个问题就变成了只出现一次的数字,最后再使用异或操作符遍历两个组,就能得到m和n的值了。
整个思路中最关键的就是:两个不相等的数异或的结果必然有个比特位的值是1,对于这个比特位,既然两个数在这个位上的值不一样,那我们就可以通过这个比特位将数据分成两组,这两个数就被分到了不同的组中。
不用担心相等的两个数被分到不同的组中,因为对于相等的两个数来说,任何一个比特位上的值都是一样的,所以它们不可能被分到两个组中。
在代码实现中还有一个问题,就是如何找到异或值中的某个值为 1 的比特位,这里有个简单的办法,计算lowbit
,lowbit = m & -m
。
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
unsigned int tmp = 0;
for (auto e : nums) tmp ^= e;
int bit = tmp & -tmp;
vector<int> ret(2);
for (auto e : nums) ret[(bit & e) == 0] ^= e;
return ret;
}
};
面试题 01.01. 判定字符是否唯一
哈希计数:
class Solution {
public:
bool isUnique(string astr) {
if (astr.size() > 26) return false;
int hash[26] = {};
for (auto ch : astr)
{
if (hash[ch - 'a']) return false;
hash[ch - 'a']++;
}
return true;
}
};
用一个int类型作为位图记录字母:
class Solution {
public:
bool isUnique(string astr) {
if (astr.size() > 26) return false;
int bitmap = 0;
for (auto ch : astr)
{
int bit = ch - 'a';
if ((bitmap >> bit) & 1) return false;
bitmap |= 1 << bit;
}
return true;
}
};
丢失的数字
求和再求差:
class Solution {
public:
int missingNumber(vector<int>& nums) {
int n = nums.size();
int sum = n * (1 + n) / 2;
for (auto e : nums) sum -= e;
return sum;
}
};
^ 位运算:
class Solution {
public:
int missingNumber(vector<int>& nums) {
int t = 0;
for (auto e : nums) t ^= e;
for (int i = 0; i <= nums.size(); i++) t^= i;
return t;
}
};
两整数之和 *
class Solution {
public:
int getSum(int a, int b) {
while (b)
{
int m = a ^ b;
int n = (a & b) << 1;
a = m;
b = n;
}
return a;
}
};
面试题 17.19. 消失的两个数字
类似 只出现一次的数字 III 。
class Solution {
public:
vector<int> missingTwo(vector<int>& nums) {
int m = 0;
for (int i = 1; i <= nums.size() + 2; i++) m ^= i;
for (auto e : nums) m ^= e;
int bit = m & -m;
vector<int> v(2);
for (int i = 1; i <= nums.size() + 2; i++) v[(i & bit) == 0] ^= i;
for (auto e : nums) v[(e & bit) == 0] ^= e;
return v;
}
};
本篇文章的分享就到这里了,如果您觉得在本文有所收获,还请留下您的三连支持哦~
