260. 只出现一次的数字 III
1.题目介绍
2.题解
2.1 快排+遍历
思路
同本系列前几题一样
代码
class Solution {
public:
std::vector<int> singleNumber(std::vector<int>& nums) {
int count = 0;
std::vector<int> arr;
std::sort(nums.begin(),nums.end());
for (int i = 0; i < nums.size(); i++) {
if (i == nums.size() - 1 && count == 0) {
arr.push_back(nums[i]);
if (arr.size() == 2) return arr;
};
if (nums[i] == nums[i+1]) count++;
else if(count) count = 0;
else {
arr.push_back(nums[i]);
if (arr.size() == 2) return arr;
}
}
return arr;
}
};
复杂度分析
-
时间复杂度:
排序阶段的时间复杂度为 ,其中 n 是输入数组的长度。
查找单独出现数字的阶段是一个线性扫描,时间复杂度为 O(n)。
总体来说,代码的时间复杂度为 。 -
空间复杂度:
代码的空间复杂度为 O(1)。
2.2 哈希表(牺牲空间)
思路
设计(数字,出现次数)的键值对,使用哈希表进行存储
代码
class Solution {
public:
std::vector<int> singleNumber(std::vector<int>& nums) {
std::vector<int> arr;
std::unordered_map<int, int> map;
for (int num: nums){
map[num]++;
}
for (const auto& [num, occ]: map){
if (occ == 1) arr.push_back(num);
}
return arr;
}
};
复杂度分析
- 时间复杂度:O(n),其中 n 是数组 nums的长度。
- 空间复杂度:O(n),即为哈希映射需要使用的空间。
2.3 位运算(分治算法)
思路
假设数组 nums 中只出现一次的元素分别是x1和x2,如果把 nums中的所有元素全部异或起来,得到结果 x,那么一定有:
x显然不会等于 0,因为如果
因此,我们可以使用位运算
(在计算机中,负整数通常以其二进制补码的形式表示。负数的补码是通过将其对应的正数的二进制表示取反(按位取反,0变1,1变0),然后再加1得到的。
例如,-3 的二进制补码表示是
这样一来,我们就可以把
- 对于任意一个在数组
中出现两次的元素, 该元素的两次出现会被包含在同一类中; - 对于任意一个在数组
中只出现了一次的元素, 即 和 , 它们会被包含在不同类中。
这里其实就是一种分治算法的思想,将所有数组成员分为两组,并且分别处理
代码
class Solution {
public:
std::vector<int> singleNumber(std::vector<int>& nums) {
int res = 0;
int temp1 = 0, temp2 = 0;
std::vector<int> arr;
for (int num:nums){res ^= num;}
//res &= -res;
// 防止溢出
res = (res == INT_MIN ? res : res & (-res));
for (int num:nums){
if ((num & res) == res) temp1 ^= num;
else temp2 ^= num;
}
arr.push_back(temp1);
arr.push_back(temp2);
return arr;
}
};
使用res &= -res;会报错
Line 8: Char 16: runtime error: negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself (solution.cpp)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior prog_joined.cpp:17:16
代码尝试对值-2147483648进行取反操作,而这是32位有符号整数的最小值。
对这个值进行取反操作会导致溢出情况,而在C或C++中,溢出是未定义行为。溢出发生在算术操作的结果超过数据类型能够表示的最大或最小值时。
我们首先要区分一下整数取反(-res)和二进制按位取反(~res)。
具体参考:https://www.cnblogs.com/trmbh12/p/17775783.html
这里是整数取反,由于负数范围大于正数,所以会发生溢出。
但是为什么会是这个范围呢?负数为什么会多出一个呢?
具体请参考:https://www.cnblogs.com/trmbh12/p/17773283.html
这里使用
因为INT_MIN 对应的是1000.....,只有最高位为1,所以不需要res & (-res),直接等于原数即可,同时可以避免溢出问题>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了