645. 错误的集合

博客讨论了如何使用位运算高效地解决LeetCode的‘错误的集合’问题。作者首先分享了一个超出时间限制的解决方案,然后给出了两种正确的解法,一种是通过排序和双指针,另一种是利用位运算的奇偶性。通过位运算的异或操作,找到了重复和丢失的数字。
摘要由CSDN通过智能技术生成

题目链接:   645. 错误的集合

题目描述

 

首先一开始 我是怎么做的呢?

我利用for循环遍历 写了一大堆,重复测试,最后终于写好了 我开始运行;结果我运行的时候 他告诉我

我超出时间限制

我giao,我真的会谢。

int* findErrorNums(int* nums, int numsSize, int* returnSize){
    int i = 0;
    int* num_s = malloc(sizeof(int)*2);
    int loss = 0;
    int loss_1 = 0;
    *returnSize = 2;
    int flag = 0;
    int j = 0;
    for(i = 0;i < numsSize;i++)
    {
        for(j = 0;j<numsSize-i-1;j++)
        {
            int tmp = 0;
            if(nums[j]>nums[j+1])
            {
                tmp = nums[j];
                nums[j] = nums[j+1];
                nums[j+1] = tmp;
            }
        }
    }
    for(i = 0;i<numsSize;i++)
    {
        for(j = 0;(j<numsSize&&i!=j);j++)
        {
           if(flag == 0)
            {
                if(nums[i] == nums[j])
                {
                    loss = nums[j];
                   flag = 1;
                }
            }
            
       }
    } 
    int t = 0;
    for(i = 0;i<numsSize;i++)
    {
        t++;
        if(nums[0] != 1)
        {
            loss_1 = 1;
        }
        else
        {
             if(nums[i] != t)
            {
                loss_1 = t;
                if(loss>loss_1)
                {
                    break;
              }
           }
       }
       
  }

num_s[0] = loss;
num_s[1] = loss_1;   
    return num_s;
}

 以上仅供娱乐;

下面是正解;

int* findErrorNums(int* nums, int numsSize, int* returnSize){
    int i = 0;
    int* num_s = malloc(sizeof(int)*2);
    int loss = 0;
    *returnSize = 2;
    int j = 0;
    for(i = 0;i < numsSize;i++)
    {
        for(j = 0;j<numsSize-i-1;j++)
        {
            int tmp = 0;
            if(nums[j]>nums[j+1])
            {
                tmp = nums[j];
                nums[j] = nums[j+1];
                nums[j+1] = tmp;
            }
        }
    }
    int tmp = 0;
    for(i = 0;i<numsSize;i++)
    {
        tmp = nums[i];
        if(tmp == loss)
        {
           num_s[0] = loss;
        }
        else if(tmp - loss>1)
        {
            num_s[1] = loss+1;
        }

        loss = tmp;
    }
    if(nums[numsSize-1] != numsSize)
    {
        num_s[1] = numsSize;
    }
    return num_s;
}

这只是一种解法

还有一个 位运算的方法

提到运算符,我们要先熟悉掌握 位操作符的使用

& 按位与       | 按位或       ^ 按位异或            (注:他们的操作数必须是整数。)

1.按位与

int main()
{
	int a = 3;
	int b = -5;
	int c = a & b;
	printf("c = %d\n", c);
	return 0;
}

思考一下,C会等于多少呢?

结果是 C = 3

3 的原 反 补码 (正数的原反补码相同)
00000000000000000000000000000011
-5的原码
10000000000000000000000000000101  -5的原码
11111111111111111111111111111010  -5的反码
11111111111111111111111111111011  -5的补码
00000000000000000000000000000011  3的补码
& 按位与  表示 2 进制位 相同位都为 为一 不同位(都是零也为零)为零 那么计算结果 为
00000000000000000000000000000011   为  3
所以C = 3;

2.按位或

int main()
{
	int a = 3;
	int b = -5;
	int c = a | b;
	printf("c = %d\n", c);
	return 0;
}

再思考:

11111111111111111111111111111011  -5的补码
00000000000000000000000000000011  3的补码
|  按位或  表示 2 进制位  一位为一 就为一 两个都是零 才为零;11111111111111111111111111111011   (-5的补码)

3.按位异或

int main()
{
	int a = 3;
	int b = -5;
	int c = a ^ b;
	printf("c = %d\n", c);
	return 0;
}


11111111111111111111111111111011 - 5的补码
00000000000000000000000000000011  3的补码
^ 按位或  表示 2 进制位  相同为零 相异为一;
11111111111111111111111111111000(-8的补码)
 

以上都是 这些为位运算符的基本使用规则; 下面来说一次 他们的简单性质(遵循交换律和结和律)

a^a = 0
0^a = a
a^b^a = b

掌握了这些,现在我们可以来对一开始的题目进行分析求解;

通过分析我们可以知道:

重复的数字在数组中出现 2 次,丢失的数字在数组中出现 0 次,其余的每个数字在数组中出现 1 次。由此可见,重复的数字和丢失的数字的出现次数的奇偶性相同,且和其余的每个数字的出现次数的奇偶性不同。如果在数组的 n 个数字后面再添加从 1 到 n 的每个数字,得到 2n 个数字,则在 2n 个数字中,重复的数字出现 3 次,丢失的数字出现 1 次,其余的每个数字出现 2 次。根据出现次数的奇偶性,可以使用异或运算求解。

代码 如下

int* findErrorNums(int* nums, int numsSize, int* returnSize) {
    int* errorNums = malloc(sizeof(int) * 2);
    int xorSum = 0;
    for (int i = 1; i <= numsSize; i++) 
    {
        xorSum ^= i;               //增加一组(1->n)的数据 
        xorSum ^= nums[i - 1];     //for 循环结束 这里xorSum 的结果应该就是 
                                   //重复数字 ^ 缺失数字                                    
    }
    int lowbit = xorSum & (-xorSum);//我们知道 负数与正数 的区别是
                                    //负数的补码是正数按位取反 加一得到的
                                    //所以 这里lowbit就为xorSum的最低位;
                                    //而 xorSum = 重复数字 ^ 缺失数字  
                                    //也就是说   lowbit 就是 重复数字和缺失数字最低的不同位
   //利用lowbit可以将数组分成两组
   //第一组的每个数字 a都满足 a & lowbit==0,
   //第二组的每个数字 b都满足 b & lowbit!=0 


    int num1 = 0, num2 = 0;
    for (int i = 0; i < numsSize; i++) 
    {
        if ((nums[i] & lowbit) == 0) 
    {
            num1 ^= nums[i];
        } else {
            num2 ^= nums[i];
        }
    }
    for (int i = 1; i <= numsSize; i++) 
    {
        if ((i & lowbit) == 0) 
    {
            num1 ^= i;
        } else {
            num2 ^= i;
        }
    }
    int* ret = malloc(sizeof(int) * 2);
    *returnSize = 2;
    for (int i = 0; i < numsSize; i++) 
    {
        if (nums[i] == num1) 
    {
            ret[0] = num1, ret[1] = num2;
            return ret;
        }
    }
    ret[0] = num2, ret[1] = num1;
    return ret;
}

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/set-mismatch/solution/cuo-wu-de-ji-he-by-leetcode-

下面我们来看一下标准答案 

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/set-mismatch/solution/cuo-wu-de-ji-he-by-leetcode-solution-1ea4/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值