Description
Given an array of integers, every element appears three times except for one, which appears exactly once. Find that single one.
Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?
思路
题意:给定一个数组,其中有一个数出现一次,其他数出现三次,要求在时间复杂度为O(n)空间复杂度为O(1)的条件下,找出这个出现一次的数。
题解:
方法一:因为除了一个数外,其他每个数都出现三次,因此枚举每一位二进制位,统计每一位上这些数为1的数总共有多少个,然后与3相模,如果余1,证明这个出现一次的数的二进制在这位上为1。(此法是通用方法,适用于除一个数外,其他数出现k次,那么只需模k即可)
class Solution { public: //12ms int singleNumber(vector<int>& nums) { int res = 0; for (int i = 0;i < 32;i++){ int cnt = 0; int mask = 1 << i; for (int j = 0;j < nums.size();j++){ if (nums[j] & mask){ cnt++; } } if (cnt % 3){ res |= mask; } } return res; } };
方法二:用两个变量记录所有数二进制位中哪些位为1出现一次,哪些二进制位为1出现两次,之所以只需两个,是因为同一个数最多只出现三次,因此我们可以选定状态 00 -> 01 -> 10来记录,那么我们用ones表示哪些位为1出现一次(模3后出现一次),用twos表示哪些位为1出现两次(模3后出现一次),当ones和twos某一二进制位上同时为1说明这位为1出现了三次,那么我们此时将ones和twos的这位二进制位清0,最后ones就是答案。
class Solution { public: //9ms int singleNumber(vector<int>& nums) { int ones = 0,twos = 0,threes = 0; for (unsigned int i = 0;i < nums.size();i++){ //以下两句代码不能颠倒次序,如若颠倒,则一个数先记录于ones, //然后twos的值依赖于ones及这个数,那么这个数就被统计了两次 twos |= (nums[i] & ones); //记录有哪些二进制位为1且出现两次存于twos ones ^= nums[i]; //记录有哪些二进制位为1且出现一次存于ones //以下三句是清零操作 threes = ones & twos; ones &= ~threes; twos &= ~threes; } return ones; } };
另外,方法二代码可精简如下:ones与twos的含义与上述相同。
class Solution { public: //9ms int singleNumber(vector<int>& nums) { int ones = 0,twos = 0; for (unsigned int i = 0;i < nums.size();i++){ //ones&~twos以及twos&~ones都是为了清零操作,两者二进制位都为1时清零 ones = (ones ^ nums[i]) & (~twos); twos = (twos ^ nums[i]) & (~ones); } return ones; } };