[Leetcode] Single Number I | Single Number II

题目一: Given an array of integers, every element appears twice except for one. Find that single one.

要求:O(n)时间复杂度,不使用额外空间。


一个数组中其他元素都出现两次,只有一个出现一次,要找到那个出现一次的。因为是需要线性复杂度,就不能用两重循环,又因为不能用额外空间,所以不能用hash,参考了网上的境地算法,都是用位操作实现的。所以这是一个考察位操作功底的题目。

先介绍异或操作,即两个数相同取0,不同则取1;性质:任何一位二进制数同 1 异或都是取反,任何一位二进制数同 0 异或都保持不变这条性质最直观的应用就是不用额外变量交换两个数。

void exchange(int &a, int &b)

 {
    b=a^b;                  //b'每位的0和1 表示a,b对应位相同和不同
    a=a^b;                  //a^0得到相同的,a^1得到b的那位
    b=b^a;                  //同样b'^a(b)会得到a
} 

还有个性质是a ^b ^c = a ^ (b ^ c) = (a ^ b) ^ c,就是说1^2^3^4^4^3^2=2^2^3^3^4^4^1=1,这就知道怎么解决上面的问题了。

int singleNumber(int A[], int n) 
 {
    // Note: The Solution object is instantiated only once and is reused by each test case.
    if(A == NULL ){
        return 0;
    }
    int result = A[0];
        
    for(int i = 1; i < n; i++){
        result = result ^ A[i];
    }
    return result;
}


题目二:Given an array of integers, every element appears three times except for one. Find that single one.

要求:O(n)时间复杂度,不使用额外空间。


这次是其他数都出现了三次,只有一个出现一次,找到这个数。上面的方法就不能用了,但思路还是位操作,利用量ones, twos , threes别保存各个二进制位上 1 出现一次、两次、三次的分布情况,如代thress=0100,这表明第三位出现过三次1。我们用与或操作记录某位上出现的1,用与操作记录1再次出现的情况 最后只需返回变量ones就行了。码如下:

int singleNumber(int A[], int n) 
{  
	int ones = 0, twos = 0, threes = 0;  
	for(int i = 0; i < n; i++)  
	{  
		threes = twos & A[i];		//1已经出现两次并且再次出现  
		twos = twos | ones & A[i];	//1出现两次,ones & A[i]=1的表示 那位还是1 
		ones = ones | A[i];		//1出现一次, ones | A[i]=1 表示那位上出现了1
              
		twos = twos & ~threes;           //当某一位出现三次后,我们就从出现两次中消除该位  
		ones = ones & ~threes;           //当某一位出现三次后,我们就从出现一次中消除该位  
	}  
	return ones;                             //twos, threes最终都为0.ones是只出现一次的数  
}  

       还在网上发现了一种通用解法,处理其他数据出现k次。 int 数据共有32位,可以用32变量存储 这 N 个元素中各个二进制位上 1 出现的次数,最后在进行模k操作,如果为1,那说明这一位是要找元素二进制表示中为 1 的那一位。代码如下:

 int singleNumber(int A[], int n) {
        int bitnum[32]={0};
        int res=0;
        for(int i=0; i<32; i++){
            for(int j=0; j<n; j++){
                bitnum[i]+=(A[j]>>i)&1;
            }
            res|=(bitnum[i]%3)<<i;
        }
        return res;
    }

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

分析:如果能够把原数组分为两个子数组。在每个子数组中,包含一个只出现一次的数字,而其他数字都出现两次。如果能够这样拆分原数组,按照前面的办法就是分别求出这两个只出现一次的数字了。

我们还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。因为其他数字都出现了两次,在异或中全部抵消掉了。由于这两个数字肯定不一样,那么这个异或结果肯定不为0,也就是说在这个结果数字的二进制表示中至少就有一位为1。我们在结果数字中找到第一个为1的位的位置,记为第N位。现在我们以第N位是不是1为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第N位都为1,而第二个子数组的每个数字的第N位都为0现在我们已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字,而其他数字都出现了两次。因此到此为止,所有的问题我们都已经解决。

unsigned int FindFirstBitIs1(int num)
{
      int indexBit = 0;
      while (((num & 1) == 0) && (indexBit < 32))
      {
            num = num >> 1;
            ++ indexBit;
      }
 
      return indexBit;
}
bool IsBit1(int num, unsigned int indexBit)
{
      num = num >> indexBit;
 
      return (num & 1);
}
void FindNumsAppearOnce(int data[], int length, int &num1, int &num2)
{
      if (length < 2)
            return;
 
      // get num1 ^ num2
      int resultExclusiveOR = 0;
      for (int i = 0; i < length; ++ i)
            resultExclusiveOR ^= data[i];
 
      // get index of the first bit, which is 1 in resultExclusiveOR
      unsigned int indexOf1 = FindFirstBitIs1(resultExclusiveOR);
 
      num1 = num2 = 0;
      for (int j = 0; j < length; ++ j)
      {
            // divide the numbers in data into two groups,
            // the indexOf1 bit of numbers in the first group is 1,
            // while in the second group is 0
            if(IsBit1(data[j], indexOf1))
                  num1 ^= data[j];
            else
                  num2 ^= data[j];
      }
}











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值