136:只出现一次的数
题目:
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
求解方法:
- map平衡二叉树。统计每个元素出现的次数,返回次数为1的元素即可。时间复杂度为,空间复杂度为.
- unordered_map散列表。统计每个元素出现的次数,返回次数为1的元素即可。时间复杂度为,空间复杂度为.
- 异或位运算。将数组中所有元素进行异或运算,返回最后异或的结果即可。时间复杂度为,空间复杂度为.
注意事项:
- 利用异或的原因:
代码:
class Solution {
public:
int singleNumber(vector<int>& nums) {
return singleNumberWayIII(nums);
}
// way1:map统计;时间复杂度为o(nlogn),空间复杂度为o(n).
int singleNumberWayI(vector<int>& nums) {
int n = nums.size();
if(n == 0){
return -1;
}
// count by map
map<int,int> m;
for(int num:nums){
m[num]++;
}
int res = -1;
// return when count = 1
for(map<int,int>::iterator it = m.begin();it != m.end(); it++){
if(it->second == 1){//important:不是*it->second而是it->second
res = it->first;
break;
}
}
return res;
}
// way2:unordered_map;时间复杂度为o(nlogn),空间复杂度为o(n).
int singleNumberWayII(vector<int>& nums) {
int n = nums.size();
if(n == 0){
return -1;
}
// count by unordered_map
unordered_map<int,int> m;
for(int num:nums){
m[num]++;
}
// return when count = 1
int res = -1;
for(unordered_map<int,int>::iterator it = m.begin(); it != m.end(); it++){
if(it->second == 1){//important:不是*it->second而是it->second
res = it->first;
break;
}
}
return res;
}
// way3:异或位运算;时间复杂度为o(n),空间复杂度为
int singleNumberWayIII(vector<int>& nums) {
int n = nums.size();
if(n == 0){
return -1;
}
int res = 0;
for(int num:nums){
res ^= num;
}
return res;
}
};
137:只出现一次的数II
题目:
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
求解方法:
- map平衡二叉树。统计每个元素出现的次数,返回次数为1的元素即可。时间复杂度为,空间复杂度为.
- unordered_map散列表。统计每个元素出现的次数,返回次数为1的元素即可。时间复杂度为,空间复杂度为.
- 数组去重后数据和的3倍与原数组和的差除以2。时间复杂度为,空间复杂度为。原因是假设数组共个数,其和记作,去重剩余个数,其和记作.去重后数组和的三倍即是个数的和,减去个数的和,剩余的就是只出现一次的数的两倍。即:
- 三进制不进位加法。上一题中利用异或运算,异或也称作二进制不进位加法,此题只要利用三进制不进位加法即可。时间复杂度为,空间复杂度为.
注意事项:
- 第三种利用数组和的方法,在求和的时候和药用long类型的数据,否则会越界.
- set和map<int,int>以及unordered_map<int,int>的迭代器的使用的不同方法。
代码:
class Solution {
public:
int singleNumber(vector<int>& nums) {
return singleNumberWayIV(nums);
}
// way1:map统计;时间复杂度为o(nlogn),空间复杂度为o(n).
int singleNumberWayI(vector<int>& nums) {
int n = nums.size();
if(n == 0){
return -1;
}
// count by map
map<int,int> m;
for(int num:nums){
m[num]++;
}
int res = -1;
// return when count = 1
for(map<int,int>::iterator it = m.begin();it != m.end(); it++){
if(it->second == 1){//important:不是*it->second而是it->second
res = it->first;
break;
}
}
return res;
}
// way2:unordered_map;时间复杂度为o(nlogn),空间复杂度为o(n).
int singleNumberWayII(vector<int>& nums) {
int n = nums.size();
if(n == 0){
return -1;
}
// count by unordered_map
unordered_map<int,int> m;
for(int num:nums){
m[num]++;
}
// return when count = 1
int res = -1;
for(unordered_map<int,int>::iterator it = m.begin(); it != m.end(); it++){
if(it->second == 1){//important:不是*it->second而是it->second
res = it->first;
break;
}
}
return res;
}
// way3:sum of array of no duplication and sum of array;时间复杂度为o(nlogn),空间复杂度为o(n).
int singleNumberWayIII(vector<int>& nums) {
int n = nums.size();
if(n == 0){
return -1;
}
// get sum of array and remove the duplications meanwhile
set<int> s;
long sum1 = 0;//important:long类型
for(int num:nums){
sum1 += num;
s.insert(num);
}
// get sum of no duplications
long sum2 = 0;
for(set<int>::iterator it = s.begin(); it != s.end(); it++){
sum2 += (*it);//important:是*it不是it
}
return (3*sum2 - sum1)/2;
}
// way4:三进制不进位加法;时间复杂度为o(n),空间复杂度为o(1).
/*(1)用 one 记录到当前处理的元素为止,二进制1出现“1次”(mod 3 之后的 1)的有哪些二进制位;
(2)用 two 记录到当前计算的变量为止,二进制1出现“2次”(mod 3 之后的 2)的有哪些二进制位。
(3)当 one 和 two 中的 某一位 同时为1时表示该二进制位上1出现了3次,此时需要清零。即用二进制模拟三
进制运算。
(4)最终 one 记录的是最终结果。
*/
int singleNumberWayIV(vector<int>& nums) {
int one = 0,two = 0, three = 0;
for(int num:nums){
one = one ^ num & ~two;
two = two ^ num & ~one;
}
return one;//important:返回的是one(因为需要返回的是出现一次的数)
}
};
260:只出现一次的数III
题目:
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。
求解方法:
- 排序。(1)判断第一个元素和最后一个元素;(2)判断中间元素,只出现一次的元素的特点是既不等于前面的数也不等于后面的数。时间复杂度为,空间复杂度为.
- unordered_map散列表。统计每个元素出现的次数,返回次数为1的元素即可。时间复杂度为,空间复杂度为.
- 位运算1。(1)通过异或运算找到只出现一次的两个元素的异或值记作mask = a^b,a和b因为不同,所以a和b的二进制中至少存在一位不同,即mask中至少存在一个1;(2)通过mask = mask & (-mask)找到mask中最右边的1;(3)通过mask对这两个数分组,找到两个数并更新结果。时间复杂度为,空间复杂度为。
- 位运算2。(1)通过异或运算找到只出现一次的两个元素的异或值记作mask = a^b,a和b因为不同,所以a和b的二进制中至少存在一位不同,即mask中至少存在一个1;(2)通过mask = mask & (~mask+1)找到mask中最右边的1;(3)通过mask对这两个数分组,找到两个数并更新结果。。时间复杂度为,空间复杂度为.
注意事项:
代码:
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
return singleNumberWayIV(nums);
}
//way1:sort+check;时间复杂度为o(nlogn),空间复杂度为o(1);
/*
(1)判断第一个元素和最后一个元素;
(2)判断中间元素,只出现一次的元素的特点是既不等于前面的数也不等于后面的数。
*/
vector<int> singleNumberWayI(vector<int>& nums) {
int n = nums.size();
vector<int> res;
if(n == 0){
return res;
}
//sort
sort(nums.begin(),nums.end());
// check the first number
if(nums[0] != nums[1]){
res.push_back(nums[0]);
}
// check the last number
if(nums[n-1] != nums[n-2]){
res.push_back(nums[n-1]);
}
// check the middle element
for(int i=1; i<n-1;i++){
if(nums[i]!=nums[i-1] && nums[i]!=nums[i+1]){
res.push_back(nums[i]);
}
}
return res;
}
// way2:unordered_map;时间复杂度为o(nlogn),空间复杂度为o(n).
vector<int> singleNumberWayII(vector<int>& nums) {
int n = nums.size();
vector<int> res;
if(n == 0){
return res;
}
// count
unordered_map<int,int> m;
for(int num:nums){
m[num]++;
}
// push to the res when count = 1
for(unordered_map<int,int>::iterator it = m.begin(); it != m.end(); it++){
if(it->second == 1){
res.push_back(it->first);
}
}
return res;
}
// way3:位运算;use n = n & (-n) to find the first 1 from right;时间复杂度为o(n),空间复杂度为o(1).
vector<int> singleNumberWayIII(vector<int>& nums) {
int n = nums.size();
vector<int> res(2,0);
if(n == 0){
return res;
}
// 异或
int mask = 0;
for(int num:nums){
mask ^= num;
}
// find first 1 from right
mask = mask & (-mask);
// find the result
for(int num:nums){
if((mask & num) == mask){
res[0] = res[0] ^ num;
}else{
res[1] = res[1] ^ num;
}
}
return res;
}
// way4:位运算;use n = n & (~n+1) to find the first 1 from right;时间复杂度为o(n),空间复杂度为o(1).
vector<int> singleNumberWayIV(vector<int>& nums) {
int n = nums.size();
vector<int> res(2,0);
if(n == 0){
return res;
}
// 异或
int mask = 0;
for(int num:nums){
mask ^= num;
}
// find first 1 from right
mask = mask & (~mask+1);
// find the result
for(int num:nums){
if((mask & num) == mask){
res[0] = res[0] ^ num;
}else{
res[1] = res[1] ^ num;
}
}
return res;
}
};