位运算解决一类题消失的数字

x33g5p2x  于2021-11-22 转载在 其他  
字(3.1k)|赞(0)|评价(0)|浏览(188)

面试题 17.04. 消失的数字

难度简单45收藏分享切换为英文接收动态反馈

数组nums包含从0n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?

**注意:**本题相对书上原题稍作改动

示例 1:

**输入:**[3,0,1]
**输出:**2

示例 2:

**输入:**[9,6,4,2,3,5,7,0,1]
**输出:**8

起初刚开始做这一道的题的时候首先想到的是排序,排完序后在遍历数组如果后面一个减前一个不等于于1那么我们就可以确定缺少的数字数哪一个但是又想了一下这样的话时间就取决于排序算法这显然不满足题目的要求。于是变换了另一种方法:定义一个变量tmp将数组中的每一个元素累加起来,在定义一个变量sum将0到n的数字累加起来在用sum-tmp便得到了消失的那一个数字

代码实现如下

int missingNumber(int* nums, int numsSize){
int sum=0;
int tmp=0;
for(int i=0;i<numsSize;i++)
{
    tmp+=nums[i];//将数组中的每一个元素累加起来
}
for(int i=0;i<numsSize+1;i++)
{
sum+=i;//将0到n的数字累加起来
}
return sum-tmp;//sum-tmp得到的便是消失的那一个数字
}

当然如果你对位运算比较熟悉这道题还可以用位运算来实现,位运算中的^对应的二进制中相同为0相异为1比如3^3==0,3的二进制序列为011那么根据上述规则很容易等出结果为0,其次就是位运算满足交换律比如说1^2^3^2这个运算结果也可以等于2^2^1^3代码如下:

#include<stdio.h>
int main()
{
    int tmp1=2^2^1^3;
    int tmp2=1^2^3^2;
    printf("%d\n",tmp1);
    printf("%d\n",tmp2);
}

这表明 位运算满足交换律,其二就是任何数^0都等于他本身。

有了上面的这些基础我们就可以来做这道题了

首先我们可以定义一个变量tmp并将他赋值成0在和数组中的每一个元素^我们就得到了数组中每一个元素^在一起的值由于题目告诉了我们是0到n少了1个数所以我们只需要将tmp和0到n的数字^一次便可以得到消失的那一个数字

代码如下:

int missingNumber(int* nums, int numsSize){
int tmp=0;
for(int i=0;i<numsSize;i++)
{
    tmp^=nums[i];
}
for(int i=0;i<numsSize+1;i++)
{
    tmp^=i;
}
return tmp;
}

类似的还有这一写题

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

**输入:** [2,2,1]
**输出:** 1

示例 2:

**输入:** [4,1,2,1,2]
**输出:** 4

通过次数448,532提交次数625,054

请问您在哪类招聘中遇到此题?

有了上一题的基础相信一题会变得很简单同样的我们可以定义一个变量x将它初始化为0在和数组中的每一个元素^得到的结果便是只出现一次的元素

代码如下:

int singleNumber(int* nums, int numsSize){
    int i=0;
    int x=0;
    for(i=0;i<numsSize;i++)
    {
      x^=nums[i];//和数组中的每一个元素异或
    }
    return x;

}

下面这个例子就比上面的这道要难一些了

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

示例 1:

**输入:**nums = [4,1,4,6]
**输出:**[1,6] 或 [6,1]

示例 2:

**输入:**nums = [1,2,10,4,1,4,3,3]
**输出:**[2,10] 或 [10,2]

限制:

  • 2 <= nums.length <= 10000
  • 在这道题中和上一题有所不同的是数组中有两个元素出现了如果我们重复上面的操作我们得到的是两个只出现一次数字的异或值。如果我们能够把他们分开就好了。我们可以根据^的运算规则我们可以知道异或的结果对应二进制中为1,也就是这两个只出现一次的数字对应二进制中不同的地方。我们可以根据^的结果我们可以找出这两个数字第一处不同的地方并用变量将其记录下来然后将数组分为两组重复上面的操作即可得到只出现一次的两个数字
  • 代码实现如下:
int* singleNumbers(int* nums, int numsSize, int* returnSize){
int x=0;
int *p=(int *)calloc(2,sizeof(int ));//动态内存开辟用来保存只出现一次的两个数字
for(int i=0;i<numsSize;i++)
{
    x^=nums[i];//得到只出现两次数字的异或值
}

int j=0;//记录两个只出现一次数字对应二进制中第一出不同的地方
for(j=0;j<32;j++)
{
    if( ( (x>>j) &1)==1)//
    {
        break;
    }
}

for(int i=0;i<numsSize;i++)
{
    if(((nums[i]>>j)&1)==1)
    {
        p[0]^=nums[i];//将数组分成两组重复^操作
    }
    else
    {
        p[1]^=nums[i];
    }
}
*returnSize=2;
return p;
}

下面这一道题是对上面这几道题的综合

  • 给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?

以任意顺序返回这两个数字均可。

示例 1:

**输入:** 

    [1]
**输出: **[2,3]

示例 2:

**输入:** 

    [2,3]
**输出: **[1,4]

提示:

  • nums.length <= 30000
  • 通过次数7,903提交次数13,552
  • 由于这道题的思想已经在前面说过了,这道题也就是对上面这几道提的综合
  • 在这里就不重复了
  • 代码实现如下:
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* missingTwo(int* nums, int numsSize, int* returnSize){
int x=0;
int *p=(int *)calloc(2,sizeof(int ));
for(int i=0;i<numsSize;i++)
{
    x^=nums[i];
}
for(int j=1;j<=numsSize+2;j++)
{
    x^=j;
}
int k=0;
for( k=0;k<32;k++)
{
    if( ( ( x>>k )&1 )==1)
    {
        break;
    }
}
for(int j=0;j<numsSize;j++)
{
    if( ((nums[j]>>k)&1)==1)
    {
        p[0]^=nums[j];
    }
    else
    {
        p[1]^=nums[j];
    }
}
for(int i=1;i<=numsSize+2;i++)
{
    if(((i>>k)&1)==1)
    {
        p[0]^=i;
    }
    else
    {
        p[1]^=i;
    }

}
*returnSize=2;
return p;
}

如有错误欢迎在评论区指正,希望能对大家有帮助!

相关文章

微信公众号

最新文章

更多