找出唯一出现一次的数
Posted mr_yu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了找出唯一出现一次的数相关的知识,希望对你有一定的参考价值。
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"); else printf("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); else printf("wrong args\n"); return 0; }
未完,待续。
以上是关于找出唯一出现一次的数的主要内容,如果未能解决你的问题,请参考以下文章
136 Single Number 数组中除一个数外其他数都出现两次,找出只出现一次的数