原题目
第一题
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/single-number
第二题
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,3,2]
输出: 3
示例 2:
输入: [0,1,0,1,0,1,99]
输出: 99
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/single-number-ii
第三题
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。
示例 :
输入: [1,2,1,3,2,5]
输出: [3,5]
注意:
结果输出的顺序并不重要,对于上面的例子, [5, 3] 也是正确答案。
你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/single-number-iii
题目分析
第一题
方法一:
暴力搜索,一个一个判定数组中是否有相同元素
方法二:
快排,直接比较前后两个元素是否相等,每次移动两格,如果出现不等则为答案
方法三:
哈希表法,用哈希存储,判断出现一次的元素返回
方法四:
二进制位数统计,用一个代表二进制32位的数组,将元素对应二进制位数的为1的位置在数组中加一,然后因为其他数都出现两次,所以只要取余2,剩下的即为出现一次的元素
例如:【3,3,1】
两个三,即0000····0011 即第32位和31位都是1,所以在二进制数组中统计bit[ 31 ] = 0+2,bit[ 30 ] = 0+2 ,一个一即0000····0001 即第32位是1,所以在二进制数组中统计bit[ 31 ] = 2+1=3
放法五:
位运算,用异或运算符,如果出现两次则会变为零,最后只剩下出现一次的数
第二题
方法一:
暴力搜索,一个一个判定数组中是否有相同元素
方法二:
快排,直接比较前后两个元素是否相等,每次移动三格,如果出现不等则为答案
方法三:
哈希表法,用哈希存储,判断出现一次的元素返回
方法四:
同第一题方法四,出现三次取余3
方法五:改进方法四+第一题方法五
位运算,三进制不考虑进位加法:通过定义某种运算 # ,使得 0 # 1 = 1, 1 # 1 = 2, 2 # 1 = 0。在此运算规则下,出现了 3 次的数字的二进制所有位全部抵消为 0 ,而留下只出现 1 次的数字二进制对应位为 1 。因此,在此运算规则下将整个arr中数字遍历加和,留下来的结果则为只出现 1 次的数字。
定义三个变量
ones twos threes分别代表出现一个数在二进制位上出现一次,出现两次,出现三次的情况,相当于出现一次ones的当前二进制位为 1 ,出现两次该进位进1给twos,twos当前二进制位为1,ones为0,当出现三次时,ones为1,twos为1,进位给threes为1
将ones,twos该二进制位清零,从而使出现三次的数相互抵消,保留出现一次的数
twos|=ones&nums[i];
//如果ones与nums[i]某二进制位都为真,则将该位进位给twos,代表该位出现两次
ones^=nums[i];
//如果nums[i]某二进制位出现一次,ones在某二进制位为1,出现两次某二进制位进位给twos,ones为0
threes = ones & twos;
ones &= ~threes;
twos &= ~threes;
//如果ones和twos在某二进制位都为1,则进位给threes,清空ones和twos当前位的值
改进:
进一步简化:
以上过程本质上是通过构建 3 个变量的状态转换表来表示对应位的出现次数:使所有数字“相加”后出现 3N+1 次的位 ones=1,出现3N, 3N+2次的位为 ones=0。由于 threes 其实是ones & twos的结果,因此我们可以舍弃 threes ,仅使用 ones 和 twos 来记录出现次数。
某位出现 | 1次 | 2次 | 3次 | 4次 | 5次 | 6次 | … |
---|---|---|---|---|---|---|---|
ones | 1 | 0 | 0 | 1 | 0 | 0 | … |
twos | 0 | 1 | 0 | 0 | 1 | 0 | … |
0 | 0 | 1 | 0 | 0 | 1 | … |
ones=ones^nums[i]&~twos;
//如果nums[i]在某位只出现一次,twos为假取反为真,ones为假,异或为真,与为真
//如果nums[i]在某位出现两次,twos为假取反为真,ones为真,异或为假,与为假
//如果nums[i]在某位出现三次,twos为真取反为假,ones为真,异或为假,与为假
twos=twos^nums[i]&~ones;
//如果nums[i]在某位只出现一次,ones为假取反为真,twos为假,异或为真,与为真
//如果nums[i]在某位出现两次,ones为假取反为真,twos为假,异或为真,与为真
//如果nums[i]在某位出现三次,ones为假取反为真,twos为真,异或为假,与为假
作者:jyd
链接:https://leetcode-cn.com/problems/single-number-ii/solution/single-number-ii-mo-ni-san-jin-zhi-fa-by-jin407891/
第三题
用第一题五种方法皆可(略)
符合题意的方法:
用第一题方法五位运算,得到两个出现一次数的异或值,这两值不等,所以异或值不为0,则定有某个数在二进制位上为1,某个数在二进制位上为零,所以我们可以用其中一个异或后二进制位为1的位置,与nums数组按位与,将nums分为该位置含有1和该位置不含1的情况,重新异或,从而可以区分两个出现次数为1的值,为了方便,我们找最右边第一个为1的二进制位置
for(int i=0;i<numsSize;i++)
{
Xor^=nums[i];
}
\\得到两个出现次数为1的元素的异或值
Xor&=(~Xor+1);\\或Xor&=-Xor;得到最右边一位二进制位为1的位置
for(int i=0;i<numsSize;i++)
{
if(Xor&nums[i])//区分该位置有1与无1的数组
res[0]^=nums[i];
else
res[1]^=nums[i];
}
完整代码
第一题
方法一:
int singleNumber(int* nums, int numsSize){
int i,j;
for(i=0;i<numsSize;i++)
{
for(j=0;j<numsSize;j++)
{
if(i!=j&&nums[i]==nums[j])
{
break;
}
}
if(j==numsSize)return nums[i];
}
return false;
}
方法二:
int cmp(const void *a,const void *b)
{
return *(int *)a>*(int *)b;
}
int singleNumber(int* nums, int numsSize){
qsort(nums,numsSize,sizeof(int),cmp);
int i;
for(i=0;i<numsSize-1;i=i+2)
{
if(nums[i]!=nums[i+1])
break;
}
return nums[i];
}
方法三:
struct hash_node
{
int count;
int id;
UT_hash_handle hh;
}hash_node;
struct hash_node *hash_table;
struct hash_node* hash_find(int val)
{
struct hash_node *s;
HASH_FIND_INT(hash_table,&val,s);
return s;
}
void hash_add(int val)
{
struct hash_node *s=(struct hash_node *)malloc(sizeof(struct hash_node));
s->id=val;
s->count=1;
HASH_ADD_INT(hash_table,id,s);
}
void hash_del(struct hash_node *s)
{
HASH_DEL(hash_table,s);
free(s);
}
int singleNumber(int* nums, int numsSize){
hash_table=NULL;
for(int i=0;i<numsSize;i++)
{
struct hash_node *s=hash_find(nums[i]) ;
if(s==NULL)
hash_add(nums[i]);
else
s->count++;
}
for(int i=0;i<numsSize;i++)
{
struct hash_node *s=hash_find(nums[i]);
if(s->count==1)
return nums[i];
}
return false;
}
方法四:
int singleNumber(int* nums, int numsSize){
int bit[32];
memset(bit,0,sizeof(bit));
for (int j = 0; j < 32; j++) {
for (int i = 0; i < numsSize; i++) {
int num = nums[i] >> j;
bit[j] += num & 1;
}
}
long int result = 0;
for (int i = 31; i >= 0; i--) {
result <<= 1;
result += bit[i] % 2;
}
return result;
}
方法五:
int singleNumber(int* nums, int numsSize){
int s=0;
for(int i=0;i<numsSize;i++)
{
s^=nums[i];
}
return s;
}
第二题
方法一:
通用第一题方法一暴力搜索的代码
方法二:
通用第一题方法二快排搜索的代码
i=i+2改为i=i+3
方法三:
通用第一题方法三哈希存储查找的代码
方法四:
通用第一题方法四二进制存储查找的代码
temp%2!=0改为temp%3!=0
方法五:
int singleNumber(int* nums, int numsSize){
int ones=0,twos=0,threes=0;
for(int i=0;i<numsSize;i++)
{
twos|=ones&nums[i];
ones^=nums[i];
threes=twos&ones;
ones&=~threes;
twos&=~threes;
}
return ones;
}
改进:
int singleNumber(int* nums, int numsSize){
int ones=0,twos=0;
for(int i=0;i<numsSize;i++)
{
ones^=nums[i]&~twos;
twos^=nums[i]&~ones;
}
return ones;
}
第三题
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* singleNumber(int* nums, int numsSize, int* returnSize){
int *res=(int *)malloc(sizeof(int)*2),Xor=0;
res[0]=res[1]=0;
*returnSize=2;
for(int i=0;i<numsSize;i++)
{
Xor^=nums[i];
}
Xor&=(~Xor+1);
for(int i=0;i<numsSize;i++)
{
if(Xor&nums[i])
res[0]^=nums[i];
else
res[1]^=nums[i];
}
return res;
}