leetcode刷题--基础数组--只出现一次的数字(C)

https://blog.csdn.net/qq_35546040/article/details/80284079原博主(仅仅为记录给自己看)

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

思路:本来使用简单的一个个数字比较做,后来才知道这是位运算(由于其他元素都出现了两次,因此把全部元素亦或一下,结果就出来了)方面的题,我是菜鸡阿,记得当时我导师面试我的时候也问过我这个问题,emmmm。

//小菜鸡的初始代码(错误示例):时间复杂为O(n^2)
int singleNumber(int* nums, int numsSize) {
    int i, j;
    int flag = 0;
    for(i=0; i<numsSize&&flag==0; i++){
        flag = 1;
        for(j=0; j<numsSize; j++){
            if(i == j)
                continue;
            if(nums[i] == nums[j]){
                flag = 0;
                break;
            }
        }
    } 
    if (flag == 0)
        return -1;
    else
        return nums[i-1];
}

异或(exclusive OR,缩写成xor)是一个数学运算符。它应用于逻辑运算。异或的数学符号为“⊕”,计算机符号为“xor”。其运算法则为:a⊕b = (¬a ∧ b) ∨ (a ∧¬b)
即如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
同时异或也叫半加运算,其运算法则相当于不带进位的二进制加法:二进制下用1表示真,0表示假,则异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位,所以异或常被认作不进位加法。

// 异或示例:时间复杂为O(n)
int singleNumber(int* nums, int numsSize) {
    int result=0;
    for(int i=0; i<numsSize; i++)
        result = result^nums[i];
    return result;
}
  1. 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
    示例 1:
    输入: [2,2,1,1,1,2,3]
    输出: 3
    示例 2:
    输入: [4,1,1,2,1,2,2]
    输出: 4

思路:我们把数组中所有数字的二进制表示的每一位都加起来。如果某一位的和能被3整除,那么只出现一次的数的二进制表示中对应的那一位是0;否则就是1,即按位计算每一位上1的个数,结果模3为1的那些位就是所求数二进制1所在的位。

// 时间复杂度为O(n)
int singleNumber(int* nums, int numsSize) {
    int result = 0;
    for(int i=0; i<32; i++){
        // 左移动i个位置,相当于1*2**i
        int mask = 1<<i;
        int count = 0;
        for(int j=0; j<numsSize; j++)
            // 按位与,计算数组中每个数字每位上1的个数
            if((mask&nums[j])!=0)
                count++;
        // 结果模3为1的那些位就是所求数二进制1所在的位
        if(count%3 == 1)
            result = mask|result;
    }
    return result;
}
  1. 给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。

思路: 这道题主要和1.类似,先将数组中所有元素进行同样的异或才做,但是这里有两个不同的数,所以需要从这两个数的异或值反推得到结果。

整数在计算机中是以补码(源码)的形式存储的(不管基于何种语言),所以我们可以用n&-n( 等价于n & ~(n-1) )得到n的二进制最右边的一个1,得到两个数不同的最低位。这样对于这个为1的位置,肯定可以分辨出这两个数,因为一定有两个数在这个位置1个为1,另一个为0。所以遍历整个数组,和这个数做&操作(以之前求出的不同的最低位为标志将全部数分成两个组),一组为该位上是0的,另一组为该位上是1的。把两组分别组内亦或,就可以得到我们要的两个数。

正整数的原码,反码,补码相同;负整数,反码为原码取反(除符号位),补码为反码加1(除符号为);且对一个整数的补码再求补码,等于该整数自身。

void singleNumber(int* nums, int numsSize, int *singleEnum) {
    // int singleEnum[2]={0, 0};
    int sum = 0;// 记录两个只出现一次的数的异或
    for(int i=0; i<numsSize; i++)
        sum = sum^nums[i];
    int lowest_dif = sum&(-sum);//得出两个数二进制不同的最低位
    for(int i=0; i<numsSize; i++){
        if((lowest_dif&nums[i]) == 0)
            singleEnum[0] = singleEnum[0]^nums[i];  
        else
            singleEnum[1] = singleEnum[1]^nums[i];       
    }
}

对于类似的题的其他直观的解法:(1)我们很容易就能从排序的数组中找到只出现一次的数字,但排序需要O(nlogn)时间;(2)我们也可以用一个哈希表来记录数组中每个数字出现的次数,但这个哈希表需要O(n)的空间。对比位运算几乎都是O(n)的复杂度。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值