数字出现的次数总结

1 题目1

如下图所示:找出只出现一次的两个数字其余都出现了两次
在这里插入图片描述

1.1 题目分析

从整形数组中找出只出现一次的数字,其余都出现了两次。

1.2 分析

  1. 用 0 异或所有数组中元素,找出出现一次的两个数。
    记:temp=0^ 3^ 2^ 3^ 6=2 ^ 6=4(二进制形式:0100)

  2. 找分离temp的分离标志,sep = temp & (-temp )。4&(-4)=4(0100),其中-4在内存中是以补码存储的哦,不要弄错了。

    判断语句:4 & 2 (0100 & 0010)= 0(0000) 为零,则跳过,不执行下面语句。
    4 & 6(0100&0110)=4(0100)不为零,执行下面语句,找出第一个出现一次的数。
    num[0]=0^ 2 (0000^0010)= 2(0010) 此时分离出来了2。

  3. 分离出来第二个元素。
    num[1]=temp^ num[1]=4^ 2 (0100^0010)=6 (0110)。

1.3 C语言参考代码

//0^任何都为任何数,出现两次的数字被异或除掉,只剩出现一次的数字
//分离出来只剩下两个数字异或的内容
	for (i = 0; i < numsSize; i++){
		temp = temp ^ nums[i];
	}
//接下来两个不同的数字的异或中分离出来 不同的数字
	sep = temp & (-temp );

//提取第一个数字
	for (i = 0; i < numsSize; i++){
		if (0 != (nums[i] & sep)){
			x = x ^ nums[i];//分离出来第一个数
		}
	}
	//提取第二个数字
	y = temp ^ x;//分离出来二个数
	num[0] = x;//保存找到的出现一次的数
	num[1] = y;

1.4 思路二

具体实现步骤:

  1. 先让0和全部数组元素异或;找出只出现一次的两个数的异或结果,记为ret;
  2. 找分离标志位,这里方法是假设分离标志位 为 div = 1 ;
  3. 继续寻找最合适的分离标志位:
    因为ret是异或的结果,ret中二进制位为1的那位,就代表此位对应数组中可分离一个数。此时ret对应的二进制为1 的为可作为分离标志位div ;
    图解:
    在这里插入图片描述
int div = 1;//假设分离标志位
while((div & ret) == 0)
 {
 	div <<= 1;//找出分离标志位
 }
  1. 分离两个出现一次的数
    这个代码如下:
  	   int a = 0;
       int b = 0;
       for(int n:nums)
       {
           if(div & n) a ^= n;
           else b^= n;
       }

在这里插入图片描述
在这里插入图片描述

1.5 C++代码

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
       int ret = 0;
       for(auto n:nums)
       {
           ret ^= n;
       }
       int div = 1;//假设分离标志位

       while((div & ret) == 0)
       {
           div <<= 1;//找出分离标志位
       }

       int a = 0;
       int b = 0;
       for(int n:nums)
       {
           if(div & n) a ^= n;
           else b^= n;
       }

       return vector<int>{a,b};
    }
};

2 题目2

数字出现的次数值LeetCode137题。
题目描述:
给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。

2.1 题目分析

目标:
找出只出现一次的数。
难点:
其余数字都出现了3次。

例子:

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

2.2 解决办法

出现3次的数,有一个特点:其对应二进制位相加都可以被3整除。于是我们可以利用其二进制作为出发点进行操作。
注意:

  1. 此数组为整数数组,在LeetCode环境下考虑int的32位,循环执行32次。
  2. 将数组中每个数的对应二进制位相加total += ((num>>i) & 1);,记为total。
  3. 判断相加结果total是否可以被3整除,且没有余数。如果有余数,则将对应的第i位的二进制数置为1如:ans |= (1<<i);。ans为返回的只出现一次的元素。

2.3 完整代码

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        // 位运算解法
        int ans = 0;
        for(int i=0;i < 32;++i)
        {
            int total = 0;
            for(auto num:nums)
            {
                total += ((num>>i) & 1);//求对应位1的个数
            }
            // 如果对应位取余不等于三,则说明不整除,此位对应数字出现不是三次
            if(total % 3) 
            {
                ans |= (1<<i);//将对应位置为1
            }
        }
        return ans;
    }
};

解决此问题还有一个很牛的代码,有兴趣的话可以看看,具体分析在这里

class Solution {
public:
    int singleNumber(vector<int>& nums) 
    {
        //数字电路解法
         int a = 0, b = 0;
         for (auto x : nums) 
         {
             b = (b ^ x) & ~a;
             a = (a ^ x) & ~b;
         }   
        return b;
    }
};
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值