59.数组中值出现了一次的数字
题目链接
题目描述
给定一个数字arr,其中只有有两个数字出现了奇数次,其它数字都出现了偶数次,按照从小到大顺序输出这两个数。
输入描述:
输入包含两行,第一行一个整数n(1≤n≤105),代表数组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 var n = res.split(' ')[0]; //用空格分割出arr var arr = res.split(' ')[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);