这个题来自《剑指offer》但是书上上感觉讲解不太详细,还是看博客吧(我把下面博客改写成了C++版本运行通过)

注意这个题的相关代码中,输入的数组只能有两个数出现一次,如果有第三个数出现一次,那么这个代码就会失效。

总结下算法思路:

假设原始数组中只出现一次的元素是A和B,原始数组为{A,E,C,D,C,D,E,B}

主要是利用异或的交换律。

先把所有数字按次序进行异或运算,得到的结果必然是A⊕B,因为其他元素都是出现两次的,根据异或运算的交换律有

A⊕E⊕C⊕D⊕C⊕D⊕E⊕B=A⊕B⊕C⊕C⊕D⊕D⊕E⊕E=A⊕B

这个A⊕B中可能有多个比特位是1,我们取最右侧的一个bit(其实其他bit也行)

先把原始数组拆成两份,怎么拆呢?利用上面的那个A⊕B的最右侧的值为1的bit位(其他位置为0),

和这个bit位&结果为非0的归为1组并进行异或运算(异或运算的初始值为0),

和这个bit位&结果为0的归为另外1组并进行异或运算(异或运算的初始值为0)

在归为一组的同时进行异或运算,所以其实这里再次使用了异或运算的交换律,

因为C,D,E都出现两次,无论归为哪一组,异或结果都是0,我们可以不关心。

好了,所以其实就是剩下的A和B与上面的bit进行运算的结果是最关键的。

因为A⊕B的最右侧的bit位表示了A和B在二进制的某位上的不一致,也就是在这个位上必定一个是1,一个是0,

所以当A和(A⊕B的最右侧的bit位)进行&运算时,要么相等,要么不相等。

相等时丢入其中一组去做异或运算,因为其他元素都是出现两次,所以最终异或结果肯定是这个只出现一次的数

不相等时丢入其中一组去做异或运算,因为其他元素都是出现两次,所以最终异或结果肯定是这个只出现一次的数

文章转载自:

https://segmentfault.com/a/1190000004886431

260.Single Number II 原题链接

  • 本题其实算是比较简单,在 leetcode 上也只是 medium 级别,ac 率也很高,最好先自己尝试,本文只是单纯的记录一下自己整体的思路;

  • 在阅读本文章之前,最好先解锁本题的简单模式 136.Single Number,这对理解本题有较大的帮助;

  • 还有很多细节作者都没有写进去,是为了留给读者一点思考的空间,其实是因为懒

  • 由于作者个人水平等原因,出现错误在所难免,还望各位看官海涵。

注意 Note 中的第一个条件:The order of the result is not important.,这个条件非常重要,这关系到算法的正确性。


然后给出整个算法的具体思路,假设数组中两个不同的数字为 A 和 B;

  1. 通过遍历整个数组并求整个数组所有数字之间的 XOR,根据 XOR 的特性可以得到最终的结果为 AXORB = A XOR B

  2. 通过某种特定的方式,我们可以通过 AXORB 得到在数字 A 和数字 B 的二进制下某一位不相同的位;因为A 和 B 是不相同的,所以他们的二进制数字有且至少有一位是不相同的。我们将这一位设置为 1,并将所有的其他位设置为 0,我们假设我们得到的这个数字为 bitFlag;

  3. 那么现在,我们很容易知道,数字 A 和 数字 B 中必然有一个数字与上 bitFlag 为 0

  4. 因为bitFlag 标志了数字 A 和数字 B 中的某一位不同,那么在数字 A 和 B 中的这一位必然是一个为 0,另一个为 1

  5. 而我们在 bitFlag 中将其他位都设置为 0,那么该位为 0 的且只出现一次的数字& bitFlag 就等于 0,而该位为 1 的且只出现一次的数字与上 bitFlag 就等于 bitFlag

  6. 现在问题就简单了,我们只需要在循环一次数组,将与上 bitFlag 为 0 的数字进行 XOR 运算,与上 bitFlag 不为 0 的数组进行独立的 XOR 运算。那么最后我们得到的这两个数字就是 A 和 B。

先给出具体实现,引用自 proron's Java bit manipulation solution,我修改了部分代码以便于理解:

#include<iostream>
using namespace std;
int* singleNumber(int* nums,int length)
{int AXORB = 0;for (int i = 0; i<length; i++){AXORB ^= nums[i];}//这里利用的是异或运算的交换律,最终结果等于两个只出现一次的元素的抑或//即:AXORB=A 异或 B// pick one bit as flagcout << "AXORB=" << AXORB << endl;cout << "AXORB-1=" << AXORB - 1 << endl;int bitFlag = (AXORB & (~(AXORB - 1)));cout << "bigFlag=" << bitFlag << endl;//使用这个bitFlag来给原始数组进行分类,分成2类,//之所以分成两类是希望每一个类都有一个只出现一次的数即A或者Bint *res = new int[2];res[0] = res[1] = 0;//这里不要忘记初始化。for (int i = 0; i<length; i++){cout << "i=" << i << endl;cout << "nums[i]=" << nums[i] << endl;if ((nums[i] & bitFlag) == 0){cout << "res[0]=" << res[0] << endl;res[0] ^= nums[i];//这里再次利用异或运算的交换律。//因为分成了两类,所以其中一类不停地运算,最终结果一定是只出现一次的数。}//例如6^3^5^5^3=6^3^3^5^5=6//下面的else中同理else {cout << "res[1]=" << res[1] << endl;res[1] ^= nums[i];}cout << "-------------------------" << endl;}return res;
}int main()
{int nums[8] = { 2,4,3,6,3,2,5,5 };int *result = singleNumber(nums,sizeof(nums)/sizeof(nums[0]));cout << "--------main----------" << endl;cout << result[0] << endl;cout << result[1] << endl;cin.get();cin.get();return 0;
}

接下来,我们一行行的解析代码:

int AXORB = 0;
for(int num: nums){AXORB ^= num;
}

这段代码在 136.Single Number 已经解析过,很容易理解最后得到的结果:假设数组中不同的数字为 A 和 B,那么 最后得到的结果是 A XOR B。

随后的这一行代码是整个算法中的难点:

//pick one bit as flag
int bitFlag = (AXORB & (~ (AXORB - 1)));

这一行代码的作用是:找到数字 A 和数字 B 中不相同的一位,并将该位设置为 1,其他位设置为 0;
根据 XOR 的定义,我们知道,在 AXORB 中,为 1 的位即 A 和 B 不相同的位,AXORB 中为 0 的位即 A 和 B 中相同的位
所以,要找到 A 和 B 中不相同的位,只需要找到在 AXORB 中从右往左第一个为 1 的位,保留该位并将其他位置为 0 即可。

//其实这一行与下面的代码等价,但是论逼格就差远了(手动斜眼
public static int f(int num){int times = 0;while(num > 0){if(num % 2 == 1){break;}times++;num = num >> 1;}return 1 << times;
}
//下面这个返回 true
System.out.println(Stream.iterate(1, num -> num + 1).limit(Integer.MAX_VALUE).allMatch(num -> f(num)==(num & (~(num -1)))));

我们可以把这一行代码解析为三步:

int tmp0 = AXORB - 1;
  • 假设 AXORB 从右往左出现的第一位非 0 数字出现在第k位,那么数字 AXORB 可以表示为,可能等于 0:

如果 a0 = 1;那么问题非常简单, AXORB - 1 可以表示为:

int tmp1 = ~tmp0;

int bitFlag = AXORB & tmp1;

由前面假设我们知道 a0=1,所以很明显, bitFlag = 1;

如果 a0 != 1,我们同样很容易得出这一行代码的作用就是将数字 AXORB 的从右到左第一个出现 1 的位置为 1,其他位全部置为 0。其实一点都不容易,只不过用 laTex 写数学公式好蛋疼啊,所以偷下懒

到这里,整个算法基本上就没什么难点了,大家自行理解吧。

我了个大槽,我本来只是想试试新学的 laTex,结果他喵的就写了这么多。大写加粗的坑

leetcode 算法解析(一):260. Single Number III(C++版本和自己的注解)相关推荐

  1. 【?异或】LeetCode 260. Single Number III

    LeetCode 260. Single Number III Solution1: 博客转载自:http://www.cnblogs.com/grandyang/p/4741122.html 这道题 ...

  2. leetcode 260. Single Number III | 260. 只出现一次的数字 III(位运算:分组异或)

    题目 https://leetcode.com/problems/single-number-iii/ 题解:分组异或 参考1:讨论区题解 you know you can eliminate dou ...

  3. 【LeetCode】-- 260. Single Number III

    问题描述: https://leetcode.com/problems/single-number-iii/ 在一个数组里面,只有两个元素仅出现过1次,其余都出现过两次.找出出现仅一次的那两个(a, ...

  4. LeetCode 260. Single Number III

    转载请注明出处:http://www.cnblogs.com/liangyongrui/p/6354552.html 异或的妙用. 一开始读题不仔细,以为有很多的孤立数字. 没想到就两个- - 然后参 ...

  5. 260. Single Number III

    题目: Given an array of numbers nums, in which exactly two elements appear only once and all the other ...

  6. Leet Code OJ 260. Single Number III [Difficulty: Medium]

    题目: Given an array of numbers nums, in which exactly two elements appear only once and all the other ...

  7. LeetCode Single Number III(位操作)

    问题:给出一个数组,有两个数只出现一次,其它都出现两次. 思路:先对数组求异或,得到两个数异域的结果.然后确定差异的最低位.再将数组与这个差异位作异域,得到其中的一个数.再与异域结果异域后即得到另外一 ...

  8. leetcode python3 简单题136. Single Number

    1.编辑器 我使用的是win10+vscode+leetcode+python3 环境配置参见我的博客: 链接 2.第一百三十六题 (1)题目 英文: Given a non-empty array ...

  9. 【leetcode79】Single Number III

    题目描述: 给定一个数组,里面只有两个数组,只是出现一次,其余的数字都是出现两次,找出这个两个数字,数组形式输出 原文描述: Given an array of numbers nums, in wh ...

最新文章

  1. 科技互联网公司越来越重视数学了,贾扬清等大牛如是说!
  2. 优化SQLServer--表和索引的分区(二)
  3. RabbitMQ 安装和简单测试
  4. 一天搞定CSS:背景background--03
  5. oracle如何复制表的索引,Oracle表与索引管理
  6. table control的修改/排序/删除功能实现实例
  7. SpringBoot(十三)-- 不同环境下读取不同配置
  8. fullcalendar 显示的时间间隔只有四十五分钟_【体能新视点】——女子篮球运动员比赛期间的心率、血乳酸浓度和时间运动分析...
  9. ASP.NET MVC 5调用其他Action
  10. linux系统修改只读文件权限如(etc/hosts)文件
  11. fstatfs/statfs详解
  12. js 对象 浅拷贝 和 深拷贝
  13. 遗传算法及c++实现
  14. batchplot插件用法_batchplot插件怎么安装 batchplot插件安装教程
  15. 利用伊藤引理计算一般资产的微分方程
  16. Linux常用英文单词
  17. 吃透String的intern方法
  18. c语言已知斜率 求倾角,直线与方程(一)倾斜角与斜率
  19. <<算法很美>>——(七)——DFS典题(一):水洼数目
  20. 如何彻底卸载2345全家桶?

热门文章

  1. C# Redis实战(七)
  2. SQL Cookbook:一、检索记录(8)在SELECT语句中使用条件逻辑
  3. ionic3实战之—Radio传值
  4. qrcode方法生成的二维码安卓手机长按不识别
  5. 安装mysql出现由于找不到VCRUNTIME140_1.dll,无法继续执行代码,请重新安装程序
  6. 安卓代码迁移:ActionBarActivity: cannot be resolved to a type
  7. OpenCV中GPU模块使用
  8. [转]边框回归(Bounding Box Regression)详解
  9. 线程(CreateThread)
  10. win2008 server_R2 自动关机 解决