Leetcode中等:260. 只出现一次的数字 III

题目介绍

给你一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。

你必须设计并实现线性时间复杂度的算法且仅使用常量额外空间来解决此问题。

/**

 * Note: The returned array must be malloced, assume caller calls free().

 */

int*  singleNumber(int* nums, int numsSize, int* returnSize) {

   

}

示例 1:
输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。

示例 2:
输入:nums = [-1,0]
输出:[-1,0]

示例 3:
输入:nums = [0,1]
输出:[1,0]

提示: 

  • 2 <= nums.length <= 3 * 104
  • -231 <= nums[i] <= 231 - 1
  • 除两个只出现一次的整数外,nums 中的其他数字都出现两次 

 260. 只出现一次的数字 III - 力扣(LeetCode)


解题思路

        这题我们可以用位运算异或的思路来解题,0异或任何数,值不变;两个相同的数异或,值为0。

0 ^ a = a;        a ^ a = 0

第一步 

        首先先定义ret,让ret异或数组所有元素,出现两次的元素就会异或为0,ret的值为只出现一次的两个元素异或的值。

ret = 0 ^ 1 ^ 1 ^ 2 ^ 2 ^ 3 ^ 3 ^ 4 ^ 5 = 4 ^ 5

代码如下:

    int ret = 0;
    for(int i = 0;i<numsSize;i++){
        ret ^= nums[i];
    }

第二步

        现在ret的值是两个元素异或的值,所有我们要想办法把两个数分离。根据异或的性质我们可以知道二进制中ret为1的位,在那个位上的两个数一定不同,找出第m位ret为1的位,根据这点将原数组分为两组。

如下可知,在第一位ret就为1,那么原数组中二进制第一位是1的元素分为一组,二进制第一位是0的元素分为一组,那么相同元素的一定为分为一组,两个不同元素的一定不为一组,两组分别异或就可以找出两个分离的元素了。

int类型4的二进制:0000 0000 0000 0000 0000 0000 0000 0100

int类型5的二进制:0000 0000 0000 0000 0000 0000 0000 0101

ret的二进制:         0000 0000 0000 0000 0000 0000 0000 0001

找出第m位为1的代码如下: 

    int m = 0;
    size_t l = 1;
    // 利用异或分组,找出ret第m位为1的位
    while (m < 32) {
        if (ret & (l << m))
            break;
        else
            m++;
    }

错误:runtime error: left shift of 1 by 31 places cannot be represented in type ‘int’

在此题中(1<<m)中的1要为无符号数,因为在32位系统中,有符号整数二进制的第32位表示符号位,左移的位数不允许超过整数本身的位数,否则就会报以上错误。

第三步

        因为函数的返回值是int*,我们要将分离的两个数赋值给新的数组,再返回回去。为此我们需要用malloc函数在堆上开辟sizeof(int)* 2大小的空间,来存放数组用于返回。

 代码如下:

    int* arr = (int*)malloc(sizeof(int) * 2);
    arr[0] = arr[1] = 0;

    for (int i = 0; i < numsSize; i++) {
        if (nums[i] & (l << m)) {
            arr[0] ^= nums[i];
        } else {
            arr[1] ^= nums[i];
        }
    }

代码实现

int* singleNumber(int* nums, int numsSize, int* returnSize) {
    int ret = 0;
    int* arr =(int*)malloc(sizeof(int)*2);

    //0与nums数组的所有元素异或,最后ret的值是只出现一次的两个元素异或的值
    for(int i = 0;i<numsSize;i++){
        ret ^= nums[i];
    }

    int m = 0;
    size_t l = 1;//1需要无符号位,否则会报错
    //利用异或分组,找出ret第m位为1的位
    while(m < 32){
        if(ret & (l<<m)) break;
        else m++;
    }

    //分离
    arr[0] = arr[1] = 0;
    for(int i = 0;i < numsSize;i++){
        if(nums[i] & (l<<m)){
            
            arr[0] ^= nums[i];
        }
        else{
            arr[1] ^= nums[i];
        }
    }

    *returnSize = 2;
    return arr;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值