一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到这一个数

279 阅读2分钟

异或计算(无进位相加)

  • 0异或N=N N异或N=0

  • 满足交换律和结合律(具体操作和执行的次序无关)

    public static void printOddTimesNum1(int[] arr){ int eor = 0; for (int cur : arr){ eor ^= cur; } System.out.println(eor); }

  • 只有一个是奇数项,将所有元素都异或,相同元素就会消失,只剩下奇数的元素,利用到了异或的交换律和结合律

2,一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到这两个数 例子

1011,0110,0110,10111000,0001】
1,让所有元素异或运算,得到eor变量,这个利用相同位置上1的个数,如果是奇数个,则为1,同理,偶数个10
2,第一步得到的eor=1001,因为eor的末尾元素为1,则将所有末尾为1的元素进行第二次异或,得到eor’,具体是【10111011,0001】,则eor'=0001,eor'也为第一个奇数个元素
3,将eor和eor'进行异或,eor=1001,eor'=0001,则二者计算得到1000为第二个奇数个的元素

引出新的问题

  • 上面例子中的eor=1001,我们提取最后面位置上的元素 1,是如何实现的呢?

  • 例子(方法1)

    01101000如何操作变成00001000呢?提取到指定位数的1,其余位数全部清零 假设 a = 01101000 1,计算 a-1,目的是打散最末尾的数字1,a-1 = 01100111 2,计算 a同或a-1 = 01100000,目的是清楚原始最右侧的1 3,计算 a异或(a同或a-1)= 00001000

    a & (~a +1) a = 01101000, a取反得到10010111,再 +1 得到10011111 a & (~a +1) = 01101000 & 10011111 = 00001000 获取最右边的 1

两种方式

  • a & (~a + 1)

  • a ^ (a | (a-1))

    class Solution {public: vector singleNumbers(vector& nums) { int temp = 0; std::vectorans{}; for (int i = 0; i < nums.size(); ++i) { temp ^= nums[i]; } int second = temp & (~temp + 1);//提取最右边的1 int first = 0; for (auto temp_ : nums) { if ((temp_ & second)==0){ first ^= temp_; } } ans.emplace_back(first); ans.emplace_back(first ^ temp); return ans; }};