1.问题描述:找出数列中唯一一个出现一次的数,其余得数都出现两次。

分析:

  1)最笨的方法当然是穷举了:

#include <stdio.h>int array[] = { 1 , 3, 3, 1, 5, 5 , 6};int
get_only()
{int *ptmp1, *ptmp2;int *arrend = array + sizeof(array) / sizeof(int);for(ptmp1 = array; ptmp1 != arrend; ptmp1 ++){for (ptmp2 = array; ptmp2 != arrend; ptmp2++){if (ptmp2 != ptmp1 && *ptmp2 == *ptmp1){break;}}if (ptmp2 == arrend){return ptmp1 - array;}} if (ptmp1 == arrend){return -1;}
}int
main(int argc, char *argv[])
{int rst = get_only();if (rst == -1)printf("no such number\n");elseprintf("only num is : %d\n", array[rst]);return 0;
}

  2)异或操作 (^):

  性质: 0 ^ 0 = 0, 1 ^ 1 = 0, 0 ^ 1 = 1, 0 ^ A = A;

  总结起来就是按位异或 相同为0,不同为1。

  这样一来明显得出,相同的俩个数异或得0,不同的两个数异或一定不为0(多少另算),0和任和数异或都得其本身(0 ^ A = A)。

  由此得出另一种方法,将数组中的所有元素进行异或操作,则剩下的一个必定是唯一一个只出现一次的数:

#include <stdio.h>int array[] = { 1 , 3, 3, 1, 5, 5 , 6 };int get_only2()
{int ret = array[0];int i;printf("len is %d\n", len);for (i = 1; i < sizeof(array) / sizeof(int); i++){printf("ret is %d\n", ret);ret = ret ^ array[i];}return ret;
}int
main(void)
{int ret = get_only2();printf("only number is %d\n", ret);return 0;
}

  btw:运用异或操作还可完成不借助其它参数,交换两个数的值: (用 g++编译,gcc编译不过)

#include <stdio.h>void
swap(int &a, int &b)
{a = a ^ b;b = a ^ b;a = a ^ b;
}int
main(void)
{int a = 1;int b = 2;printf("before swap: a %d, b %d\n", a, b);swap(a, b);printf("after swap:  a %d, b %d\n", a, b);return 0;
}

-----------------------------------------------------------------------分割线----------------------------------------------------------

2.问题描述:数列中,只有两个整数只出现一次,其余的出现两次,找出这两个整数.

  有了上边的铺垫,很容易想到用 异或 操作,通常回想到将所有数异或。

  但是发现,若这两个数分别为A, B,则异或后 只能消除其它存在两次的数,但是结果为A ^ B,仍然找不出A, B。

  此时我们希望A和B, 分别出现在两个数组中,并且这两个数组中的各自的其它成员,是两两相同的。然后这两个数组分别各自异或操作,则数组各自只剩下A和B,这样便找出来了。

  但是我们如何分出这样的两个数组呢?

  根据之前分析得出,A和B不相等则 A ^ B 的结果必然不为0,那么这个结果必定可以用16进制表示,并且至少某一位上为1.我们假设从右向左的第x位不为0,即为1.(0x001001...,1的位置为x)

  那么肯定A的第x位为1,B的为0;或者相反。同理,除A,B以外的这些数的第x位上或者为0,或者为1. 此时便可根据第x位为0或者为1,将整个数列分成两个数组且分别包含A和B,姑且叫arrA, arrB。

  这样一分,我们还发现,arrA中除A,以外是两两相同的,arrB除B以外也是两两相同的。最极端的情况是arrA只包含A或者arrB只包含B。但是这不重要,重要的是我们得到了两个数组分别包含A和B且其余两两相同。

  于是,我们分别将arrA中元素异或操作得到A,将arrB中元素异或操作得到B。

  步骤:(g++编译)

  1)先得到A ^ B (get_oxr_rst)

  2)找到第x位上的1,这里的技巧是得到一个mask,只处理这一位 (get_mask)。

  3)用mask & arr[idx], 找到这一位为1的所有元素,并异或操作。

  4)B = orx_rst ^ A.

#include <stdio.h>int
get_oxr_rst(int *arr, int len)
{int rst = 0;int idx;for(idx = 0; idx < len; idx ++){rst ^= arr[idx];} return rst;
}int
get_mask(int oxr_rst)
{int mask = 1;while((mask & oxr_rst) == 0) {mask <<= 1;} return mask;
}int
get_rst(int *arr, int len, int &A, int &B)
{if (len < 2){return 0;}int oxr_rst = get_oxr_rst(arr, len);int mask = get_mask(oxr_rst);int idx;for (idx = 0; idx < len; idx++){if (mask & arr[idx]){A ^= arr[idx];}}B = A ^ oxr_rst;return 1;
}    int
main(int argc, char *argv[])
{int arr[] = { -4, 2, 2, 4 , 3, 5, 5, 3};int A = 0, B = 0;int rst = get_rst(arr, sizeof(arr) / sizeof(int), A, B);if (rst)printf("different number is A %d and B %d\n", A, B);elseprintf("wrong args\n");return 0;
}

View Code

3.问题描述:数列中,有三个整数只出现一次,其余的出现两次,找出这三个整数.

  思路1):同样如果按照上边思路来,假设三个不同的值是A, B, C。首先得到 rst = A^B^C

  找到rst的十六进制从右到左第一个1。如果A,B,C在这个位上的值为分别001或010或100。则这种情况下仍可以求出一个mask,

  进而可以将数列分成两组,其中一个必定包括在该位为1的数,假设为A,然后遍历数组,将 arr[idx] & mask != 0的所有值异或,这样便得到了A,然后就简化成了第二个问题。

  但是rst的十六进制从右到左第一个1,还可能是111这个情况,这样一来便无法用思路1来解,因为我们不能知道A,B,C的第x位是否是111,还是001或010或100,只有是后者才能用常规思路。

  到这里得到结论 思路1是行不通的。那么看思路2.

  

  思路2):

  (1)另x = (A ^ B), y = (B ^ C), z = (C ^ A)

  (2)我们有 (A^B)^(B^C)^(C^A) = x ^ y ^ z = (rst ^ C) ^(rst ^ A) ^(rst ^ B) = 0

  (3)x != 0, y != 0, z != 0 , 且 x != y != z, 因为两个相等的数 异或 才等于零,而A, B, C是互不相等的。

  (4)x ^ y ^ z = 0 且 x != y != z != 0, 那么x, y, z每位上的组合只能是000或110,这样才能保证x ^ y ^ z = 0,

     同时又不可能所有位组合全为 000,那样 x = y = z = 0与已知矛盾.由此得  x, y, z中十六进制从右向左数第一次肯定会出现110。

    假设首次出现110组合是从右向左数第m位,且两个1来自 x和y,则可以得到z的m位是0,并且,z第一次出现1的位置在m的左边,即可能是

    从右向左数(m + k = n, k > 0)的位置。举例 若x = 0x1001, y = 0x0001, 则一定有z = 0x1000。

    好了,在继续解释之前,我们要知道 rst ^ arr[i] 的结果集中,肯定包含x, y, z.因为x = rst ^ C, y = rst ^ A, z = rst ^ B,而A, B, C是arr中元素,

    同时,如果我们将 rst ^ arr[i]得到结果集中的每个新元素,按照其从右向左数第一次出现的1是否在第n位上进行分组,则可以得到两组数v1,v2,其中一组包含

    z,另外一组包含x,y。假设包含z的为v1, 包含x,y的为v2. 那么同时这样又将arr数组元素分为两组arr1, arr2,其中arr1中包含B, arr2中包含A,C.我们将 arr1中的所有

    元素异或操作,从而可以得到B。

  (5)找到一个了,另外两个就好找了。

#include <stdio.h>struct diff_values
{int first;int second;int third;
};static int
get_oxr_rst(int *arr, int len)
{int rst = 0;int idx;for(idx = 0; idx < len; idx ++){rst ^= arr[idx];} return rst;
}//a new method to get the mask without using a loop
static int
get_first_onebit_mask(int value)
{return value & ~(value - 1);
}static int
get_mask(int *arr, int len, int oxr_rst)
{int idx;int mask = 0;/*if arr[i] = arr[j] then oxr_rst ^ arr[i] = oxr_rst ^ arr[i] thenget_first_onebit_mask(oxr_rst ^ arr[i]) = get_first_onebit_mask(oxr_rst ^ arr[j]) thenthis operation cross out all arr[i] = arr[j].then we get:mask = get_first_onebit_mask(oxr_rst ^ (arr[i])  //(arr[i] = A)^ get_first_onebit_mask(oxr_rst ^ arr[j]) //(arr[j] = B)^ get_first_onebit_mask(oxr_rst ^ arr[k]) //(stt[k] = C)then mask is what we want whose first "1" is just on postion n*/for (idx = 0; idx < len; idx++){//mask ^= get_first_onebit_mask(oxr_rst ^ arr[idx]);int tmpmask = get_first_onebit_mask(oxr_rst ^ arr[idx]);mask ^= tmpmask;}return mask;
}static int
get_rst2(int *arr, int len, struct diff_values &dv)
{if (len < 2){return 0;}int oxr_rst = get_oxr_rst(arr, len);int mask = get_first_onebit_mask(oxr_rst);int idx;int first = 0;for (idx = 0; idx < len; idx++){if (mask & arr[idx]){first ^= arr[idx];}}dv.second = first;dv.third = first ^ oxr_rst;return 1;
}int
get_rst(int *arr, int len, struct diff_values &dv)
{if (len < 3){return 0;}int oxr_rst = get_oxr_rst(arr, len);int mask = get_mask(arr, len, oxr_rst);int idx;int first = 0;for (idx = 0; idx < len; idx++){if (get_first_onebit_mask(oxr_rst ^ arr[idx]) == mask) //this operation equals to get v1.
        {first ^= arr[idx]; //this equals to the oxr operation in arr1 which contains B as the example// and the result is one of value of A, B , C.
        }}//when we get there, it means we get one of the three unique numbers;//then we get the left two numbers;//exchange first with arr[len - 1], for (idx = 0; idx < len; idx++){if (first == arr[idx]){arr[idx] = arr[idx] ^ arr[len - 1];arr[len - 1] = arr[idx] ^ arr[len - 1];arr[idx] = arr[idx] ^ arr[len - 1];break;}}dv.first = first;//only pass len - 1 values into arr, so that we can delete value firstget_rst2(arr, len - 1, dv);return 1;
}    int
main(int argc, char *argv[])
{int arr[] = { 3, 3, -4,5, 5, 8, 9 };struct diff_values dv;int rst = get_rst(arr, sizeof(arr) / sizeof(int), dv);if (rst)printf("different number is A %d , B %d , C %d\n", dv.first, dv.second, dv.third);elseprintf("wrong args\n");return 0;
}

参考:

  http://www.cnblogs.com/luxiaoxun/archive/2012/09/08/2676610.html

  https://www.lijinma.com/blog/2014/05/29/amazing-xor/

  http://baike.baidu.com/link?url=SeBrbp2jNqe1N-hF_j4UWt953O7ra73Seo__jW2uEbXeRuo-wLTKkROZZAV_jCTLICKw7oSQQRQmZovRCaFYkcC3MIS2jSd7wR9kVVIbjKm

  

以后碰到类似的题,继续补充,欢迎指正与指点。

  

  

  

  

 

  

转载于:https://www.cnblogs.com/newbeeyu/p/5898258.html

找出唯一出现一次的数相关推荐

  1. 位运算:找出唯一成对的数、找出落单的数

    1.找出唯一成对的数 题目: 1- 1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次.每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空 ...

  2. ACMNO.11 一个数如果恰好等于它的因子之和,这个数就称为“完数“。 例如,6的因子为1、2、3,而6=1+2+3,因此6是“完数“。 编程序找出N之内的所有完数,并按下面格式输出其因子

    写在前面,心得感悟~ 代码越来越有难度! 这个ACM题,我调试了 将近50次~ 一个小时! 真的是,年纪轻轻的搞什么ACM呀! 关于题的解决思路放在下面再写吧! 题目描述 一个数如果恰好等于它的因子之 ...

  3. 如何在10亿个整数中找出前1000个最大的数?

    作者:vincent-duan,专注 Java,沉迷开源,架构师社区合伙人! 面试题目:如何在10亿个整数中找出前1000个最大的数. 我们知道排序算法有很多: 冒泡算法:通过两层for循环,外层第一 ...

  4. 找出1000以内的所有完数。

    1 ''' 2 一个数如果恰好等于它的因子之和,这个数就称为"完数".例如6=1+2+3.编程 3 找出1000以内的所有完数. 4 ''' 5 # 报错是因为之前定义了sum= ...

  5. 如何在10亿个整数中找出前1000个最大的数(TopN算法)

    面试题目:如何在10亿个整数中找出前1000个最大的数. 我们知道排序算法有很多: 冒泡算法:通过两层for循环,外层第一次循环找到数组中最大的元素放置在倒数第一个位置,第二次循环找到第二大的元素放置 ...

  6. C语言学习之一个数如果恰好等于它的因子之和,这个数就称为“完数”。例如,6的因子为1,2,3,而6=1+2+3,因此6是“完数”。编程序找出1000之内的所有完数。

    一个数如果恰好等于它的因子之和,这个数就称为"完数".例如:6的因子为1,2,3,而6=1+2+3,因此6是"完数".编程序找出1000之内的所有完数,并按下面 ...

  7. RUNOOB python练习题19 找出1000以内的所有完数

    用来练手的python 练习题,原链接 : python练习实例19 题干: 一个数如果恰好等于它的因子之和,这个数就称为"完数".例如6=1+2+3.编程找出1000以内的所有完 ...

  8. python实现一个数如果恰好等于它的因子之和,这个数就称为“完数” 。例如, 6的因子为 1、2、3,而 6=1+2+3,因此 6 是完数。编程找出1000之内的所有完数

    一个数如果恰好等于它的因子之和,这个数就称为"完数" . 例如, 6 的因子为 1.2.3,同时6=1+2+3,因此 6 是完数. 编程找出 1000 之内的所有完数,并输出该完数 ...

  9. JavaScript_找出数组下标并返回下标数

    找出数组下标并返回下标数 代码演示: <script>var str = 'Hello world, Hello javascript.';var newArr = getChar(str ...

  10. 一个数如果恰好等于它的因子之和,这个数就称为 完数 。例如6=1+2+3.编程 找出1000以内的所有完数。

    一个数如果恰好等于它的因子之和,这个数就称为 "完数 ".例如6=1+2+3.编程 找出1000以内的所有完数. 思路:首先求出一个数的所有因子 在判断这个数的所有因子之和与它本身 ...

最新文章

  1. 今晚直播写代码|英伟达工程师亲授如何加速YOLO目标检测
  2. 深化对KMP算法的理解
  3. 《HTTP权威指南》学习笔记——HTTP报文
  4. markdownpad2 html渲染组件出错_「万字长文」一文吃透React SSR服务端同构渲染
  5. matlab浮点数求绝对值_MATLAB仿真阵列天线切比雪夫综合法(附代码)
  6. 如何用计算机处理频谱,如何使用PicoScope PC示波器对CD播放器的音频频谱进行分析...
  7. input和button放在同一行_黑龙江作家协会冯殿波散文集《足音》84情系东保卫,魅力采风行...
  8. ​突破 1nm!台积电祭出“半金属”取代硅材料;搜狗发布手语 AI 合成主播;iOS 微信 8.0.6 版本更新|极客头条...
  9. linux下常用文件传输命令(转)
  10. gcc详解以及静态,动态库的生成 1
  11. 常用数据库的字段类型及大小
  12. OpenCV教程:超详细的OpenCV入门教程,值得收藏
  13. Hive常用正则表达式
  14. Latex 公式左对齐
  15. 日本python程序员工资_年轻程序员赴日本工作有前途吗?
  16. HttpWatch(功能详细介绍)
  17. SAP 小写金额转大写
  18. 如何破解AppOps (需要root)
  19. java小折叠车测评_java的折叠车怎么样?
  20. mysql数据库 博客_mysql数据库教程--第 页-杨雨个人博客-关注互联网和搜索引擎的技术博客...

热门文章

  1. 多元梯度下降法--machine learning
  2. vs 服务容器中已存在服务_无服务器vs容器,企业如何正确选择?
  3. git add 所有修改文件_工作中Git的使用实践
  4. 估计理论(5):BLUE的定义(6.3)
  5. Kubernetes API的版本控制,分组,对象,访问控制
  6. pandas DataFrame 根据多列的值做判断,生成新的列值
  7. python解析xml格式的excel_Python 读取二进制、HTML 、XML 格式存储的 Excel 文件
  8. Sql 查询库、表、列名的语句
  9. 【模板】堆优化 + dij +pair 存储
  10. C/C++ 格式化读取和读取一行