问题描述:
在一张表里面保存了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. ArrayList 保证多线程安全

    一:使用synchronized关键字 二:使用Collections.synchronizedList();使用方法如下: 假如你创建的代码如下:List<Map<String,Obje ...

  2. decode()函数

    decode()函数简介: 主要作用:将查询结果翻译成其他值(即以其他形式表现出来,以下举例说明): 使用方法: Select decode(columnname,值1,翻译值1,值2,翻译值2,.. ...

  3. jquery工具方法swap

    swap : css交换(内部) 详细内容请点击 -> 当元素的样式为display:none时获取他的宽高

  4. android第一行代码-1.项目结构

    0.项目结构 一个简单的android项目结构大致如下 入口就是MainActivity这个类,如果对于一个陌生的项目,最好的办法是看AndroidMainifest.xml,如下 <?xml ...

  5. Netron开发快速上手(二):Netron序列化

    Netron是一个C#开源图形库,可以帮助开发人员开发出类似Visio的作图软件.本文继前文”Netron开发快速上手(一)“讨论如何利用Netron里的序列化功能快速保存自己开发的图形对象. 一个用 ...

  6. linq distinct 不够用了!

    问题引出:在实际中遇到一个问题,要进行集合去重,集合内存储的是引用类型,需要根据id进行去重.这个时候linq 的distinct 就不够用了,对于引用类型,它直接比较地址.测试数据如下: class ...

  7. 了解EF CodeFirst的Migrator功能与Migrator.Net对比

    在上一篇[数据库迁移利器:Migrator.Net]中,很多朋友提到了EF的CodeFirst也有数据库的迁移功能,说来真惭愧,玩了那么多年,至今还未去了解EF,今天来了解下CodeFirst然后与M ...

  8. django static文件的引入方式

    1. 在django project中创建 static文件夹 2.settings.py中配置要在 STATIC_URL = '/static/'  下边 STATICFILES_DIRS = [ ...

  9. MYSQL(一)

    一,概述: 1,什么是数据库: 答:数据的仓库,如:在ATM的示例中我们创建了一个 db 目录,称其为数据库. 2.什么是 MySQL.Oracle.SQLite.Access.MS SQL Serv ...

  10. <<< ajaxfileupload介绍

    ajaxfileupload,jquery的一个异步上传插件,使用此插件你可以不用建立form,他会自动生成表单,且自动设置好enctype="multipart/form-data&quo ...