数组中只出现一次的数字41

题目描述:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。例如{2,4,3,6,3,2,5,5},因为只有4,6这两个数次出现一次,所以输出4和6。

解题思路:

  1. 数组里有两个数字只出现一次,其他数字都出现两次。
  2. 异或性质:任何一个数字异或它自己都等于0.
  3. 将数组拆分成两个子数组,使得每个子数组包含一个只出现一次的数字。
  4. 首先将所有数字执行异或操作,得到result。(result是两个只出现一次的数字的异或结果)
  5. 在result中找到自右边起的第一个为1的位的位置,标记为n位。
  6. 将原数组分为两个组,第一组中每个数字的第n位都是1;第二组中每个数字的第n位都是0.
  7. 由于result是这两个只出现一次的数字的异或结果,所以它俩肯定被分配到不同的子数组。而其他出现两次的数字肯定一起被分配到用一个子数组。
  8. 再分别对两个子数组求异或,便能分别找出出现一次的数字。

测试用例:

int main(){
    //输入数组
    int arr[8] = {2, 4, 3, 6, 3, 2, 5, 5};
    //两个结果
    int num1 = 0;
    int num2 = 0;
    //查找
    FindNumAppearOnce(arr, 8, &num1, &num2);

    //输出结果
    std::cout << "num1: " << num1 << std::endl << "num2: " << num2;  //Output: 6, 4

    return 0;
}

函数实现:

//判断在number的二进制表示中从右边数起的第一位indexBit是不是1
bool IsBit1(int number, unsigned int indexBit){
    number = number >> indexBit;  //右移indexBit位
    return (number & 1);  //如果是1,返回true
} 
//在整数number的二进制表示中找到最右边是1的位
unsigned int FindFirstBitIs1(int num){
    int indexBit = 0;
    while( ( (num & 1) == 0) && (indexBit < 8 * sizeof(int))){  //等于0 及 小于最高位数 时循环
        num = num >> 1;
        ++indexBit;
    }

    return indexBit;
}
//主函数
void FindNumAppearOnce(int *data, int length, int *num1, int *num2){
    if(data == NULL || length <= 2)
        return;
    //对所有元素执行一次异或操作,获得两个不同数字的异或结果
    int resultExclusiveOR = 0;
    for(auto i = 0; i < length; ++i)
        resultExclusiveOR ^= data[i];
    //在resultExclusiveOR中找到自右边起的第一个为1的位
    unsigned int indexOf1 = FindFirstBitIs1(resultExclusiveOR);

    for(auto j = 0; j < length; ++j){
        if(IsBit1(data[j], indexOf1)) //如果元素自右边起的第indexOf1位是1,(抽象的)放在第一个数组即与num1再次执行异或操作
            *num1 ^= data[j];
        else
            *num2 ^= data[j];  //如果元素自右边起的第indexOf1位是0,(抽象的)放在第二个数组即与num2再次执行异或操作
    }
    //最终num1和num2分别保存了只出现一次的那两个数字
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值