LeetCode136/137/260 只出现一次的数字I/II/III

原题目
第一题

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 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次
ones100100
twos010010
threes001001
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Baal Austin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值