59.数组中值出现了一次的数字
题目链接
https://www.nowcoder.com/practice/200d8d789f9f431999fac795bb094356?tpId=101&&tqId=33215&rp=5&ru=/activity/oj&qru=/ta/programmer-code-interview-guide/question-ranking
题目描述
给定一个数字arr,其中只有有两个数字出现了奇数次,其它数字都出现了偶数次,按照从小到大顺序输出这两个数。
输入描述
输入包含两行,第一行一个整数n(1≤n≤10^5),代表数组arr的长度,第二行n个整数,代表数组arr,arr[i]为32位整数。
输出描述
输出出现奇数次的两个数,按照从小到大的顺序。
示例1
输入
4
1 1 2 3
输出
2 3
示例2
输入
6
11 22 11 23 23 45
输出
22 45
备注
时间复杂度O(n)O(n),额外空间复杂度O(1)O(1)。
关键技术
- 按位异或。
- 左移。
题目分析
- 用换行符分割出n,用空格分割出arr;
- 相同的数字取异或为0,不同的数字取异或为1,所以arr中所有数字的异或(sum)为两个出现奇数次的数字(str_1,str_2)的异或,即sum= str_1 ^ str_2;
- 在sum中为1的位置上,str_1和str_2必不相等,一个为1,一个为0。(若sum为11001,那么在k=0,1,4时,str_1的第0位,第1位和第4位与str_2的第0位,第1位和第4位上的数字不相等;
- 若想取出第一个不相等的位置(在上边例子中,k=0),即找出sum中第一个1出现的位置,此位置赋给k;
- 通过判断k位置上是否为1,可以把数字分成两组,一组是k位置上为1,另一组是k位置上不为1;
- 此时分别找两组数中出现奇数次数的数,即第一组中有一个数出现了奇数次,第二组中有一个数出现了奇数次。
- 一组数中,有一个数出现奇数次,那么这组数异或以后的数就是出现奇数次的数。
- 分别求出两组数的异或值后,排序输出。
var input = [];
var res = [];
while(input = readline()){
res += input + '\n';
}
//用换行符分割出n
var n = res.split('\n')[0];
//用空格分割出arr
var arr = res.split('\n')[1].split(' ');
var sum;
for(let i=0;i<n;i++){
sum ^= arr[i];
}
var s_1, s_2;
var k = 0;
//取出第一个为 1 的下标
while(!(sum & 1)){
sum = sum >> 1;
k++;
}
//找出出现了奇数次的数
for(let i=0;i<n;i++){
if((arr[i] >> k) & 1){
s_1 ^= arr[i];
}else{
s_2 ^= arr[i];
}
}
//按从小到大顺序输出
print(s_1 < s_2 ? s_1:s_2,s_1 > s_2 ? s_1:s_2);