算法题:找出整数数组中两个只出现一次的数字

问题:一个整数数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度为O(n),空间复杂度为O(1)。



分析:这是一个很新颖的关于位运算的题目。

首先考虑这个问题的一个简单版本:一个整数数组里除了一个数字之外,其他的数字都出现两次,请写程序找出这个只出现一次的数字。

这个问题的突破口在哪?题目中数组的性质是只有一个整数出现一次,其他的都出现两次。这样的话就使我们想到了异或运算的性质:任何一个数字异或它自己都等于0。也就是说如果从头到尾依次异或数组中的每一个整数,那么最终的结果刚好是那个只出现一次的数字。因为那么两两相同的整数在异或的过程中等于0抵消了。

根据上面的这个简单为题的解决方案,回到题目中的问题。如果能把原数组分为两个子数组,在每个子数组中包含一个只出现一次的数字,而其他的数字都出现两次。如果能够这样拆分原数组,那么问题就变成在拆分的两个数组中分别寻找这两个只出现一次的数字。

那么剩下的问题就是根据什么条件拆分数组?解决的办法是:把所有的整数依次异或运算,因为其他的有配对的整数异或的结果等于0,这样最后异或的结果就相当于把这两个只出现一次的整数异或,所以想要区分这两个整数,可以从它们异或的结果上区分。在它们异或的结果中找一个为1的bit位,即为第N位。根据异或的运算原理,那么这两个数的第N位肯定一个为1一个为0。根据第N位是1或者0把整个数组分成两组。就能满足每个子数组包含一个只出现一次的整数,其他的数字两两配对的出现在不同的子数组中。


算法步骤:

(1)依次异或数组中的每一个整数,得出异或运算的结果。

(2)找到异或运算结果的二进制形式中,最低位的1对应的下标值。把这个位作为区分两个整数的标准

(3)根据区分标准,把整个数组分成两个子数组,分别求它们的异或运算值,得到两个运算结果就是要求的两个只出现一次的整数。


函数声明:

bool FindTwoSingleNums(int nums[], int nLength, int &a, int &b);
bool IsBit1(int num, uint indexBit);
uint FindFirstBitIs1(int num);


代码实现:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define uint unsigned int

bool FindTwoSingleNums(int nums[], int nLength, int &a, int &b);
bool IsBit1(int num, uint indexBit);
uint FindFirstBitIs1(int num);

///<summary>
///在一个数组中找不能配对的两个整数
///<param name=num>数组名</param>
///<param name=nLength>数组长度</param>
///<param name=a>第一个不能配对的整数引用</param>
///<param name=b>第二个不能配对的整数引用</param>
///<returns>返回bool表示查找是否成功</return>
///</summary>
bool FindTwoSingleNums(int nums[], int nLength, int &a, int &b)
{
    if(nLength < 2)
    {
        return false;
    }

    int resXOR = 0;
    for(int i = 0; i < nLength; i++)
    {
        resXOR ^= nums[i];
    }//for

    uint index = FindFirstBitIs1(resXOR);
    for(int i = 0; i < nLength; i++)
    {
        if(IsBit1(nums[i], index))
        {
            a ^= nums[i];
        }
        else
        {
            b ^= nums[i];
        }

    }//for
}//FindTwoSingleNums()

///<summary>
///找出一个整数对应的二进制数出现1的最低位的下标
///<param name=num>整数</param>
///<returns>出现1的位的下标值</return>
///</summary>
uint FindFirstBitIs1(int num)
{
    assert(num != 0);
    uint index = 0;
    uint maxIndex = sizeof(num) * 8;

    //注意运算符的优先级
    while(((num & 1) == 0) && (index < maxIndex))
    {
        num >>= 1;
        ++index;
    }//while

    return index;
}//FindFirstBitIs1()

///<summary>
///判断一个整数对应二进制的某一位是否为1
///<param name=num>整数</param>
///<param name=indexBit>要判断的位的下标</param>
///<returns>返回判断的结果</return>
///</summary>
bool IsBit1(int num, uint indexBit)
{
    num >>= indexBit;
    return num & 1;
}//IsBit1()

测试代码:

void main()
{
    int array[16] = {51,52,78,45,89,54,12,70,
                     51,52,78,45,89,54,12,74};
 
    //初始化为0,否则就会是一个随机数和数组异或运算
    //0和任何数异或运算都等于其本身
    int oneSingleNum = 0;
    int twoSingleNum = 0;
 
    FindTwoSingleNums(array, 16, oneSingleNum, twoSingleNum);
 
    printf("%d--%d\n", oneSingleNum, twoSingleNum);
 
    system("pause");
}

PS:关键词—>相同数字抑或结果为0,并根据所有元素异或结果第一个出现1的位置(出现1的最低位)把数组分成两部分。

时间: 2024-10-12 13:52:31

算法题:找出整数数组中两个只出现一次的数字的相关文章

面试题1:找出一个数组中三个只出现一次的数字

version1: 找出一个数组中一个只出现一次的数字,其他数字都出现两次:将所有数字异或,得到的结果即为只出现一次的. version2: 找出一个数组中两个只出现一次的数字,其他数字都出现两次:将所有数字异或,得到的结果即为x=a^b, index为x中第一个为1的位,则a 和b中第index为必然有一个为1,有一个为0.据此将所有数据分为两组,一组的第index为都为1,另一组第index为都为0,第一组数字异或得到a,第二组数字异或得到b. 时间复杂度为o(n),空间复杂度为o(1).

算法:找出有序数组中两数,它俩之和等于输入值

题目:已知按序排列的整数数组,输入任意数number,当数组中某两数之和等于number时,打印出两个数. 要求:复杂度为o(n) 解法: 数组已是有序排列,且两个加数一定满足条件:较小加数<= (number/2) <= 较大加数:那么只需要找出该数组的较小加数和较大加数分界index,以该分界为起点分别往左右两边逐个取值匹配. 若两数之和>number,说明较小加数还要再小,向数组的较小值方向移位取值与原较大值重新匹配. 若两数之和<number,说明较大加数还要再大,向数组的

1152: 零起点学算法59——找出一个数组中出现次数最多的那个元素

1152: 零起点学算法59--找出一个数组中出现次数最多的那个元素 Time Limit: 1 Sec  Memory Limit: 64 MB   64bit IO Format: %lldSubmitted: 990  Accepted: 532[Submit][Status][Web Board] Description 找出一个数组中出现次数最多的那个元素 Input 第一行输入一个整数n(不大于20) 第二行输入n个整数 多组数据 Output 找出n个整数中出现次数最多的那个整数(

【编程题目】找出数组中两个只出现一次的数字 ★★(自己没做出来)

61.找出数组中两个只出现一次的数字(数组)题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字.要求时间复杂度是 O(n),空间复杂度是 O(1). 思路:瞄到了一眼提示,说是位运算. 根据异或的运算性质: a ⊕ b ⊕ a = b 把所有的数字都异或一遍得到的结果就是 那两个只出现一次的数字异或的结果. 可怎么分出那两个数字就卡住了. 看了下网上答案,要根据得到的异或值把数字分为两组,再对每一组异或就可以得到这两个数字了! 代码如下: /* 61

找出数组中两个只出现一次的数字 【微软面试100题 第六十一题】

题目要求: 一个整型数组里除了两个数字机之外,其他的数字都出现了两次. 请写程序找出这两个只出现一次的数字.要求时间复杂度O(N).空间复杂度O(1). 参考资料:剑指offer第40题. 题目分析: 已知: 1.两个相同的数字异或的结果为0,即a^a = 0. 2.两个不相同的数字异或的结果的二进制中某一位为1,则这两个数字的二进制中对应位一个为0,一个为1.如3^2 = 1,对于最低位的二进制,3的最低位二进制为1,2的最低位二进制位0,则结果1的最低位二进制肯定为1. 假设原数组中只出现一

找出数组中两个只出现一次的数字

题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字.要求时间复杂度是O(n),空间复杂度是O(1). 还是理解不够深刻. 这题的主要思路还是之前的数组中只出现一次的数字的那种模式,一次遍历加上异或运算.那么这个异或的值肯定是这两个只出现一次的数字的相异或的值.那么这个值的二进制表现形式中的1的结果就表示这两个数字在该bit位上不一样.那么通过此举,我们可以以这个bit位是否为1来从整个数组中把这两个只出现一次的数字分开. 通过此举,别的出现两次的数字

数组中两个只出现一次的数字

题目: 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字,要求:时间复杂度为O(n),空间复杂度O(1) 测试样例: 输入: 8 {2,4,3,6,3,2,5,5} 输出: 4,6 解法: 使用异或解决问题:一个数异或自己等于0,异或其他数 != 0,如果是一个数字,那么一趟遍历数组异或之后的结果就是我们要的;而现在是2个数据,那么我们需要仔细考虑怎样将两个数字分开: 首先:将所有的数进行异或,得到一个结果不等于0的数字,因此这个结果数字在二进制位上必定

剑指offer 面试题40—数组中两个只出现一次的数字

题目: 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字.要求时间复杂度O(n),空间复杂度O(1). 基本思想: http://blog.csdn.net/wtyvhreal/article/details/44260321 #include <iostream> using namespace std; int Find1(int n)//从低位开始找第一个1的位置 { int index=0; while((n&1)==0 &&a

找出数组中两个只出现一次的数字!

实现代码: 1.PHP实现: <?php     $arr=array(1,1,5,3,2,2);          //因为每个元素都需要分别和其他的元素进行比较     //所以需要双重循环     for($i=0;$i<count($arr);$i++){                  for($j=0;$j<count($arr);$j++){                      //此处目的是避免数组元素和自己进行比较             if($i != $j