问题描述:
在一张表里面保存了N个ID,有N-1个ID是出现了两次的,只有一个ID只出现了一次,现在要你把这个ID找出来。如果是两个呢?
 
解法一:
我们先来解决一个的。假如ID的值的范围是1-k,当这个k不大的时候,我们可以直接开一个数组,把表扫一遍,记录下每个ID出现的次数,这样,出现一次的就的出来了,这样做的时间复杂度是O(n),空间复杂度也是O(n)。
解法二:
然而,当k很大很大时,这种做法显然就不行了。然后我们提出第二种对这种优化的方法,就是利用哈希,对每个ID哈希一遍,保存在哈希表里,然后,当我在插入的时候发现,这个ID已经出现过的时候,就把这个ID从哈希表里面删除掉,最后剩下只有一个ID,那就是只出现过一次的ID。这样时间复杂度仍然是O(n),其实时间复杂度O(n)已经没有什么可以优化的空间了。关键是空间复杂度能不能优化到O(1)呢?
解法三:
优化到O(1),也就是说,只有使用一个变量,这也就意味着,这个变量最后保存的值就是我们要找的ID。我们可以这样描述这个过程:
ID = f(A[0 : n-1])
关键就是怎样设计这个f()。
这里有一个很巧妙的方法。我们知道两个数异或有这样的性质:
a ^ a = 0
a ^ 0 = a
而且异或满足交换律,所以如果ID是这样一个集合,我们对其进行异或遍历:  a1,a1,a2,a2,a3,a3,.....ak,ak.....an,an
我们令ak是我们要找的那个ID,这个ID列表是没有顺序的,也就是说相同的ID不一定是相邻的,所以假设是这样的:
ak1 ^ ak2 ^ ak3......akn这个顺序我们是未知的,但利用异或满足交换律这个特点,可以这样:
ak1 ^ ak2 ^ ak3......akn
= a1 ^ a1 ^ a2 ^ a2 ^ ak-1 ^ ak+1 ..... ^ an ^ ak
而a1 ^ a1 = 0,所以前n-1个异或的结果为0,最后剩下
0 ^ ak = ak
所以,最后异或的结果就是只出现一个的那个ID。
 
然后,如果是两个呢?如果两个A和B,ID只出现一次呢?怎样在O(n)时间跟O(1)空间的条件下找到这两个ID呢?
像上一种方法一样,直接异或遍历,只能得到两个ID异或的结果,不能得到两个ID。
我们可以顺势就在这两个ID异或的结果上面做文章,假如异或结果为0,说明是两个相等的ID,这里先不做讨论,假如异或的结果不为0,那么结果至少有一位上为1的,这就说明,A跟B至少在这一位上是不相等的,所以,我们可以把这一位所有相同的ID分别放在一类,这样就得到一类是这一位是1的一类,我们称这是X类,另一类的这一位是0的一类,称为Y类,因为其它的n-2的ID是完全相同的,所以,如果一个ID在这一位是1,那么这两个ID一定同时被分在X类中,所以最终的结果是X类中有一个A或者B ID,Y类中有一个A或者B  ID ,但可以肯定的是A跟B一定不在同一类中,所以,我们又可以分别对这两类ID进行异或,又可以得到A和B
 

编程之美读书笔记之 -寻找出现次数为1的ID的问题的更多相关文章

  1. 编程之美读书笔记1.1——让CPU占用率曲线听你的指挥

    http://blog.csdn.net/pipisorry/article/details/36189155 <strong><span style="font-size ...

  2. 《Linux/Unix系统编程手册》读书笔记 目录

    <Linux/Unix系统编程手册>读书笔记1  (创建于4月3日,最后更新4月7日) <Linux/Unix系统编程手册>读书笔记2  (创建于4月9日,最后更新4月10日) ...

  3. 《Linux/Unix系统编程手册》读书笔记9(文件属性)

    <Linux/Unix系统编程手册>读书笔记 目录 在Linux里,万物皆文件.所以文件系统在Linux系统占有重要的地位.本文主要介绍的是文件的属性,只是稍微提及一下文件系统,日后如果有 ...

  4. 《Linux/Unix系统编程手册》读书笔记8 (文件I/O缓冲)

    <Linux/Unix系统编程手册>读书笔记 目录 第13章 这章主要将了关于文件I/O的缓冲. 系统I/O调用(即内核)和C语言标准库I/O函数(即stdio函数)在对磁盘进行操作的时候 ...

  5. 《Linux/Unix系统编程手册》读书笔记7 (/proc文件的简介和运用)

    <Linux/Unix系统编程手册>读书笔记 目录 第11章 这章主要讲了关于Linux和UNIX的系统资源的限制. 关于限制都存在一个最小值,这些最小值为<limits.h> ...

  6. 《Linux/Unix系统编程手册》读书笔记6

    <Linux/Unix系统编程手册>读书笔记 目录 第9章 这章主要讲了一堆关于进程的ID.实际用户(组)ID.有效用户(组)ID.保存设置用户(组)ID.文件系统用户(组)ID.和辅助组 ...

  7. 《Linux/Unix系统编程手册》读书笔记5

    <Linux/Unix系统编程手册>读书笔记 目录 第8章 本章讲了用户和组,还有记录用户的密码文件/etc/passwd,shadow密码文件/etc/shadow还有组文件/etc/g ...

  8. 《Linux/Unix系统编程手册》读书笔记4

    <Linux/Unix系统编程手册>读书笔记 目录 第7章: 内存分配 通过增加堆的大小分配内存,通过提升program break位置的高度来分配内存. 基本学过C语言的都用过mallo ...

  9. 《Linux/Unix系统编程手册》读书笔记3

    <Linux/Unix系统编程手册>读书笔记 目录 第6章 这章讲进程.虚拟内存和环境变量等. 进程是一个可执行程序的实例.一个程序可以创建很多进程. 进程是由内核定义的抽象实体,内核为此 ...

随机推荐

  1. hdu[1711]number sequence

    Problem Description Given two sequences of numbers : a[1], a[2], ...... , a[N], and b[1], b[2], .... ...

  2. Fullscreen API 全屏显示网页

    可参考文档:http://blog.csdn.net/tywali/article/details/8623938 脚本代码 <script type="text/javascript ...

  3. IIS部署站点相关经验总结

    IIS部署站点相关经验总结 1.IIS和.net4.0安装是有先后顺序的,应该先安装.net framework 4.0,再安装IIS.如果按相反顺序安装的话,IIS中看不到4.0相关的东西,那么只能 ...

  4. codevs 2924 数独挑战

    2924 数独挑战 http://codevs.cn/problem/2924/ 题目描述 Description "芬兰数学家因卡拉,花费3个月时间设计出了世界上迄今难度最大的数独游戏,而 ...

  5. Mersenne twister 随机数算法实现 in Scheme

    这个实现基本上是从 Wiki 上的 Python 版翻译过来的,大量使用了赋值. ;; Mersenne twister algorithm from Wikipedia ;; returns a c ...

  6. C#中TransactionScope的使用方法和原理

    在.net 1.1的时代,还没有TransactionScope类,因此很多关于事务的处理,都交给了SqlTransaction和SqlConnection,每个Transaction是基于每个Con ...

  7. [转]Python yield 使用浅析

    您可能听说过,带有 yield 的函数在 Python 中被称之为 generator(生成器),何谓 generator ? 我们先抛开 generator,以一个常见的编程题目来展示 yield ...

  8. cookie学习指南

    一.什么是cookie cookie也叫HTTP cookie,最初是用来客户端和服务器端进行会话用的,由于HTTP是一种无状态的协议,为了维持用户和跟踪用户信息,所以引入了cookie和sessio ...

  9. Kendall’s tau-b,pearson、spearman三种相关性的区别(有空整理信息检索评价指标)

    同样可参考: http://blog.csdn.net/wsywl/article/details/5889419 http://wenku.baidu.com/link?url=pEBtVQFzTx ...

  10. NVelocity解析字符串

    之前都是先从模板文件里面读取html字符串,现在要求将模板存入数据库或缓存了,怎么办呢?在网上找了下资料,终于找到解决办法. 如下: public class NVelocityHelper { // ...