leetcode刷题——只出现一次的数字123

1.

示例 1 :

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

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

输入:nums = [1]
输出:1

在重复数字的题目中,有两个很重要的结论:
        1.两个相同的数字相异或结果是0(异或是指两个数字的二进制位相同为0,不同为1)
        2.0和任何数字异或都是那个数字

        所以第一道题,可以直接让数组所有的元素异或,得到的结果就是只出现过一次的数字:

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

2.

示例 1:

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

输入:nums = [0,1,0,1,0,1,100]
输出:100

        第二题没有想到简单的方法,还没研究答案的做法,是暴力求解的,思路大概是:把数组进行冒泡排序,如果一个元素既不等于左边的数也不等于右边的数,那么这个元素就是只出现一次的数。

int singleNumber(int* nums, int numsSize){
    int tmp=0;
//如果数组只有一个元素,那它就是只出现一次的数
if(numsSize<4){
    return nums[0];
}
//冒泡排序
for(int i=0;i<numsSize;i++){
    for(int j =0;j<numsSize-i-1;j++){
        if(nums[j]>nums[j+1]){
            tmp=nums[j];
            nums[j]=nums[j+1];
            nums[j+1]=tmp;
        }
    }
}
//检查只出现一次的数是否在数组的左右边界,防止越界访问
if(nums[0]!=nums[1]){
    return nums[0];
}else if(nums[numsSize-1]!=nums[numsSize-2]){
    return nums[numsSize-1];
}
//判断一个数和左右的数相不相等
for(tmp=1;tmp<numsSize-2;tmp++){
    if(nums[tmp]!=nums[tmp-1]&&nums[tmp]!=nums[tmp+1]){
        return nums[tmp];
    }
}
return 0;
}

        需要注意的是:1.如果数组只有一个元素,直接返回[0],2.对下标是[1]~[numsSize-2]的元素进行for循环判断是否和左右想等,下标是[0]和[numsSize-1]的需要单独判断一边。

3.

​​​​​示例 1:

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

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

输入:nums = [0,1]
输出:[1,0]

        同样运用异或的结论:但是有一个问题,现在有两个只出现一次的数,直接异或最后肯定不会得到这两个数,所以我们要把两个数分开,所以对于只出现一次的两个数,可以通过寻找这两个数的二进制的不同位来把两个数分开(因为两个数不相等,一定有一位二进制位不相同),进而把两个数分开。对于出现两次的两个数,它们的二进制位一模一样,所以分到一个组。

        现在我们就得到了两组数,两组的数都和《只出现一次的数字1》一样,进而每组数异或得到两组中只出现一次的数,也就是答案。

这里拿题目的实例1举例:

        如图所示,因为3,5 第二个二进制位不同,所以根据这一位来分类,0的一类,1的一类,

最后这两组异或处理最后得到答案。

int* singleNumber(int* nums, int numsSize, int* returnSize) {
    int number=0;
    int result=0;
    *returnSize=2;
    int num1=0,num2=0;
    for(number=0;number<numsSize;number++){
        result =result^nums[number];
    }
    for(number=0;number<32;number++){
        if((result>>number)&(1)==1){
            break;
        }
    }
    for(int i =0;i<numsSize;i++){
        if((nums[i]>>number)&(1)==1){
            num1=nums[i]^num1;
        }else{
            num2=num2^nums[i];
        }
    }
    int* array=malloc(2*sizeof(int));
    array[0]=num1;
    array[1]=num2;
    return array;
}

这里再解释一下前两个for循环,最后的for循环就是两组数的一直异或比较好理解。

        第一个for循环的目的是得到两个只出现一次的数的异或值(相同的数异或完都变成0了,0接着异或不影响),第二个就是找到这两个只出现一次的数的二进制位哪一位不同,(按位于1,如果结果为1说明这个二进制位不同)并且根据这个来分组。

        感谢阅读,希望文章对你有所启发,如有错误欢迎评论。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值