需求
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,3,2]
输出: 3
示例 2:
输入: [0,1,0,1,0,1,99]
输出: 99
思路
除了想到了hashmap,没有想到其他方法。本来想用 ^ 的方式,但是不知道怎么处理 3 次,参考了答案,真心牛逼。其实出现几次的问题本质就是实现几进制数,我们看看官方的解释,我们就学会了如何去模拟一个N进制加法的实现(解释中的 #)
1. 【二进制下不考虑进位的加法】:本题为 136. Single Number 的拓展,136 题中我们用到了异或运算。
实际上,异或运算的含义是二进制下不考虑进位的加法,即:
0 xor 0 = 0+0 = 0,
0 xor 1 = 0+1 = 1,
1 xor 0 = 1+0 = 1,
1 xor 1 = 1+1 = 0(不进位)。
2. 【三进制下不考虑进位的加法】:通过定义某种运算 #,使得
0 # 1 = 1,
1 # 1 = 2,
2 # 1 = 0。
在此运算规则下,出现了 3 次的数字的二进制所有位全部抵消为 0,而留下只出现 1 次的数字二进制对应位为 1。
因此,在此运算规则下将整个 arr 中数字遍历加和,留下来的结果则为只出现 1 次的数字。
3. 代码分析: 请结合代码注释和图表理解。
ones ^= num:记录至目前元素num,二进制某位出现 1 次(当某位出现 3 次时有 ones = 1 ,与 twos = 1 共同表示“出现 3 次”);
twos |= ones & num:记录至目前元素num,二进制某位出现 2 次 (当某位出现 2 次时,twos = 1 且 ones = 0 );
threes = ones & twos:记录至目前元素num,二进制某位出现 3 次(即当 ones 和 twos 对应位同时为 1 时 three = 1 )。
one &= ~threes, two &= ~threes:将 ones, twos 中出现了 3 次的对应位清零,实现 “不考虑进位的三进制加法” 。
代码实现
/*
* 需求
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,3,2]
输出: 3
示例 2:
输入: [0,1,0,1,0,1,99]
输出: 99
gcc singleNumberIII.c -g -o a.exe -DDEBUG
*/
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#ifdef DEBUG
#define LOG(fmt, args...) fprintf(stdout, fmt, ##args)
#else
#define LOG(fmt,...)
#endif
#define TRUE 1
#define FALSE 0
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) > (b) ? (b) : (a))
int singleNumber(int* nums, int numsSize){
int ones = 0; /*某位上出现1次1*/
int twos = 0; /*某位上出现2次1*/
int threes = 0; /*某位上出现3次1*/
int i = 0;
for(i = 0; i < numsSize; i++){
/* 看ones中那些已经置1的位,在nums[i]里面又出现了,记录下来 */
twos |= ones & nums[i];
/*将nums[i]记录到ones里,出现了就应该记录*/
ones = ones ^ nums[i];
/*ones 和 twos 里面都出现的位,说明出现了3次*/
threes = ones & twos;
/*出现3次发生进位,ones和twos对应的位清零*/
ones &= ~threes;
twos &= ~threes;
}
/*返回只出现1次的数*/
return ones;
}
void testsingleNumber(void){
printf("\n************ testsingleNumber ************ \n");
int ret = 0;
int numsSize = 0;
int nums1[4] = {2,2,2,3};
numsSize = 4;
ret = singleNumber(nums1, numsSize);
printf(" %d appear once \n", ret);
int nums2[7] = {0,1,0,1,0,1,99};
numsSize = 7;
ret = singleNumber(nums2, numsSize);
printf(" %d appear once \n", ret);
return;
}
int main(int argc, char ** argv){
testsingleNumber();
}
编译
gcc singleNumberIII.c -g -o a.exe -DDEBUG
调试输出
************ testsingleNumber ************
3 appear once
99 appear once