剑指Offer--040-数组中只出现一次的数字

链接


牛客OJ:数组中只出现一次的数字

九度OJ:http://ac.jobdu.com/problem.php?pid=1351

GitHub代码: 040-数组中只出现一次的数字

CSDN题解:剑指Offer–040-数组中只出现一次的数字

牛客OJ 九度OJ CSDN题解 GitHub代码
040-数组中只出现一次的数字 1351-数组中只出现一次的数字 剑指Offer–040-数组中只出现一次的数字 040-数组中只出现一次的数字

题意



题目描述

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字

2 4 3 6 3 2 5 5

样例输出

4 6

分析



此题考察的是异或运算的特点:即两个相同的数异或结果为0。

此题用了两次异或运算特点:

  • 第一次使用异或运算,得到了两个只出现一次的数相异或的结果。
  • 因为两个只出现一次的数肯定不同,即他们的异或结果一定不为0,一定有一个位上有1。

另外一个此位上没有1,我们可以根据此位上是否有1,将整个数组重新划分成两部分,

一部分此位上一定有1,另一部分此位上一定没有1,

然后分别对每部分求异或,因为划分后的两部分有这样的特点:其他数都出现两次,只有一个数只出现一次。因此,我们又可以运用异或运算,分别得到两部分只出现一次的数。

代码


#include <iostream>
#include <vector>
#include <bitset>

using namespace std;

#define __tmain main

#ifdef __tmain

#define debug cout

#else

#define debug 0 && cout

#endif // __tmain

class Solution
{
public:
    #define INT_SIZE (sizeof(int) * 8)

    #define IS_BIT(number, index) (((number) & (1 << (index))) >>index)

    void FindNumsAppearOnce(vector<int> array, int *num1, int *num2)
    {
        *num1 = *num2 = 0;
        if(array.size( ) < 2)
        {
            return ;
        }
        int XOR = array[0];
        for(int i = 1; i < array.size( ); i++)
        {
            XOR ^= array[i];
        }

        debug <<bitset<INT_SIZE>(XOR) <<endl;

        ///  查找到1的位置
        int index = 0, temp = 1;
        for(; index < INT_SIZE; index++)
        {
            if((XOR & temp) == temp)
            {
                break;
            }
            temp <<= 1;
        }
        debug <<index <<endl;
        if(index == INT_SIZE)
        {
            debug <<"no two numbers which once" <<endl;
            return;
        }
        debug <<"find 1 in index = " <<index <<endl;
        debug <<IS_BIT(XOR, index) <<endl;
        //  于是我们知道那两个只出现一次的数字, 第index位必然不相同
        //  因此 我们将数组划分成两部分
        //  一部分index位是0
        //  另外一部分index位是1
        *num1 = *num2 = 0;// 也可以等于XOR
        for(int i = 0; i < array.size( ); i++)
        {
            if(IS_BIT(array[i], index) == 1)
            {
                *num1 ^= array[i];
            }
            else
            {
                *num2 ^= array[i];
            }
        }
    }
};

int __tmain( )
{
    Solution solu;

    int arr[] = { 2, 4, 3, 6, 3, 2, 5, 5, };
    vector<int> vec(arr, arr + 8);

    int num1, num2;
    solu.FindNumsAppearOnce(vec, &num1, &num2);
    cout <<"RESULT : " <<num1 <<", " <<num2 <<endl;
    return 0;
}

找xor中最后一个1的位置的时候,可以用XOR & (-XOR)

由于计算机内部整数按照补码来存储,补码的表示方法是:

  • 正数的补码就是其本身
  • 负数的补码是在其原码(原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值.)的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
  • 更简单的方法是,符号位不变,从最低位开始,指导遇见第一个1,这个1后面的都不变,前面的所有位依次取反。

因此我们会发现,XOR与-XOR的区别就是最低位起始的第一个1相同,然后前面的bit位置正好相反

因此XOR & (-XOR)的值正好就是仅有一位为1,而这个位置正好是XOR最低位1的位置

代码

class Solution
{
public:
    #define INT_SIZE (sizeof(int) * 8)

    #define IS_BIT(number, index) (((number) & (1 << (index))) >>index)

    void FindNumsAppearOnce(vector<int> array, int *num1, int *num2)
    {
        *num1 = *num2 = 0;
        if(array.size( ) < 2)
        {
            return ;
        }
        int XOR = array[0];
        for(int i = 1; i < array.size( ); i++)
        {
            XOR ^= array[i];
        }

        debug <<bitset<INT_SIZE>(XOR) <<endl;

        ///  查找到1的位置
        int flag = XOR & (-XOR);
        debug <<bitset<INT_SIZE>(flag) <<endl;
        *num1 = *num2 = XOR;// 也可以等于XOR
        for(int i = 0; i < array.size( ); i++)
        {
            if((array[i] & flag) == flag)
            {
                *num1 ^= array[i];
            }
            else
            {
                *num2 ^= array[i];
            }
        }
    }
};
时间: 2024-08-28 18:17:47

剑指Offer--040-数组中只出现一次的数字的相关文章

【剑指offer】数组中只出现一次的数字(1)

转载请注明出处:http://blog.csdn.net/ns_code/article/details/27649027 题目描述: 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 输入: 每个测试案例包括两行: 第一行包含一个整数n,表示数组大小.2<=n <= 10^6. 第二行包含n个整数,表示数组元素,元素均为int. 输出: 对应每个测试案例,输出数组中只出现一次的两个数.输出的数字从小到大的顺序. 样例输入: 8 2 4 3 6 3

[剑指Offer]40.数组中只出现一次的数字

题目 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 思路 我们直到异或的性质: 任何一个数字异或他自己都等于0. 所以说我们如果从头到尾依次异或每一个数字,那么最终的结果刚好只出现一次的数字,因为成对出现的两次的数字全部在异或中抵消了. 这道题中有两个数字只出现一次.这样的话我们得到的结果就是这两个数字的异或结果.因此我们想办法把原数组分成两个子数组,使得每个子数组包含一个只出现一次的数字.这样我们就可以对这两个子数组分别异或,就能得到两个只出现一

【剑指offer】数组中只出现一次的数字

一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. /*考虑过程: 首先我们考虑这个问题的一个简单版本:一个数组里除了一个数字之外,其他的数字都出现了两次.请写程序找出这个只出现一次的数字. 这个题目的突破口在哪里?题目为什么要强调有一个数字出现一次,其他的出现两次?我们想到了异或运算的性质:任何一个数字异或它自己都等于0 .也就是说,如果我们从头到尾依次异或数组中的每一个数字,那么最终的结果刚好是那个只出现一次的数字,因为那些出现两次的数字全部在异或中

【剑指offer】数组中仅仅出现一次的数字(1)

转载请注明出处:http://blog.csdn.net/ns_code/article/details/27649027 题目描写叙述: 一个整型数组里除了两个数字之外,其它的数字都出现了两次.请敲代码找出这两个仅仅出现一次的数字. 输入: 每一个測试案例包括两行: 第一行包括一个整数n,表示数组大小.2<=n <= 10^6. 第二行包括n个整数,表示数组元素,元素均为int. 输出: 相应每一个測试案例.输出数组中仅仅出现一次的两个数.输出的数字从小到大的顺序. 例子输入: 8 2 4

【剑指offer】数组中只出现一次的数

题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 分析: 经典的异或技巧题 两个相同的数字异或的结果为0,一个数和0异或的结果是其本身,假设现在那两个不同的数字是A和B,那么将整个数组的元素依次异或得到的结果ans就是A和B的异或结果 ans的二进制中第k位的1代表A的第k位和B的第k位不同,我们现在按照第k位是否相同将原数组分成两个子数组,那么必定A和B分别分散在两个子数组中,然后将子数组依次异或,得到的结果就是A和B的值! 至于k的取值则

剑指offer (36) 数组中的逆序对

题目:在数组中的两个数字如果前面一个数字大于后面一个数字,则这两个数字组成一个逆序对 题解分析: 首先应该想到很简单的一种解法,顺序遍历数组,对每个数,逐个比较该数字和其以后的数字,T(n) = O(n^2) (1)总体的意思就是将数组分成两段,首先求段内的逆序对数量,比如下面两段代码就是求左右两端数组段内的逆序对数量 count += Merge(data, temp, first, mid);//找左半段的逆序对数目 count += Merge(data, temp, mid + 1, e

【剑指offer】数组中的逆序对

# @left part: [start, mid] # @right part: (mid, end] def merge(data, start, mid, end): if mid < start or end < mid: return 0 reverse = 0 ''' @ for start, it play as the start index of left part, and mid @ play as the end index of left part; @ mid +

剑指OFFER之数组中出现次数超过一半的数字(九度OJ1370)

题目描述: 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2. 输入: 每个测试案例包括2行: 第一行输入一个整数n(1<=n<=100000),表示数组中元素的个数. 第二行输入n个整数,表示数组中的每个元素,这n个整数的范围是[1,1000000000]. 输出: 对应每个测试案例,输出出现的次数超过数组长度的一半的数,如果没有输出-1. 样例输入:

剑指offer (29) 数组中出现次数超过一半或1/3或1/N的数字

题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字 方法一:如果把这个数字排序,那么排序之后位于数组中间的数字一定就是出现次数超过数组长度一半的数字 这个数字就是统计学中的中位数,即长度为n的数组中第n/2大的数字 在数组中得到任意第k大数字,这一问题有O(n)解,注:这里第kth个元素,kth从1开始计数,并且重复元素不去重 (1) 直接sort排序,然后定位到索引为kth-1的元素 int FindKth1(std::vector<int>& num, int kt

【Java】 剑指offer(39) 数组中出现次数超过一半的数字

本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1, 2, 3, 2, 2, 2, 5, 4, 2}.由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2. 思路 思路一:数字次数超过一半,则说明:排序之后数组中间的数字一定就是所求的数字. 利用partition()函数获得某一随机数字,其余数字按大小排在该数字的左右.若该