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

1 篇文章 0 订阅

对于这个题目:

异或的性质:对于整数a,有

  1. a^a=0
  2. a^0=a
  3. a^b^c=a^(b^c)=(a^c)^b

思路:

1、思路:

(1)对于出现两次的元素,使用“异或”操作后结果肯定为0,那么我们就可以遍历一遍数组,对所有元素使用异或操作,那么得到的结果就是两个出现一次的元素的异或结果。

(2)因为这两个元素不相等,所以异或的结果肯定不是0,也就是可以再异或的结果中找到1位不为0的位,例如异或结果的最后一位不为0。

(3)这样我们就可以最后一位将原数组元素分为两组,一组该位全为1,另一组该位全为0。 (4)再次遍历原数组,最后一位为0的一起异或,最后一位为1的一起异或,两组异或的结果分别对应着两个结果。

2、复杂度:

(1)时间复杂度:第一次循环,将所有元素异或得到对应结果,时间开销为O(n);第二次循环,找出第一次异或结果为1的位,时间开销为O(32);第三次循环,根据为1的位将元素分为两组进行异或得到两个结果,时间复杂度为O(n),所以总的时间复杂度为T(n) = 2*O(n)+O(32) = O(n)。

(2)空间复杂度:常数,因为只分配了两个空间用于结果的保存,因此空间复杂度为常数。

答案:

 

$res = 0;
//$a = [0,1,4,2,4,2,33,33];
$a = [1,2,3,4,5,1,2,3];
print_r($a);


for($i=0;$i<count($a);$i++) {
    $res ^=$a[$i];//此结果为两个不同的数异或的结果值,如a,b,c,d,a,d四个数   a^b^c^d^a^d=(a^a)^(d^d)^c^b=0^0^c^b=c^b
}
//找到异或结果为1的位,只需找一位即可比如0101,找最后一位就行;因为有两个数不同,那么肯定有位为1和0,这样异或后结果为1

//        $index = 0;
//        for(int i=0; i<32; i++) {  //找出亦或结果为1的位。  
//            if((res>>i & 1) == 1) {
//                $index = i;
//                break;
//            }
//        }

$index = 0;
while(($res%2)&1 == 0){
    $res>>1;
    var_dump($res);
    $index++;
}
var_dump($index);
$num1=0;
$num2=0;
//1,3,5,1,3, 5
//2,4,2  4
//1:0001,0001,1   num1:1
//2:0010,0001,0    num2:2
//3:0011,0001,1, num1:1^3=0001, 0011=0010=2
//4:0100,0001,0, num2:2^4=0010,0100=0110=6
//5:0101,0001,1,num1:2^5=0010,0101=0111=7
//1:0001,0001,1 num1:1^7=0001,0111=0110=6
//2:0010,0001,0 num2=6^2=0110,0010=0100=4
//3:0011,0001,1 num1:6^3 = 0110,0011=0101=5;
//比如上面找到二进制最后一位为1,现在遍历数组,把二进制最后一位为1和0的区分开来。
//如1,3,5,1,3的最后一位为1,他们为一组;2,4,2最后一位为0,他们为一组
//然后把每组的进行异或,比如上面第一组异或1^3^5^1^3=5,第二组异或:2^4^2=4
for($i=0;$i<count($a);$i++){
    if(($a[$i]>>$index) &1){
        $num1^=$a[$i];
        echo "num1:".$num1.PHP_EOL;

    } else{
        $num2^=$a[$i];
        echo "num2:".$num2.PHP_EOL;

    }
}
echo "n1:".$num1.PHP_EOL;
echo "n2:".$num2.PHP_EOL;

参考:https://www.cnblogs.com/hezhiyao/p/7539024.html

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这道题可以利用异或运算的性质来解决。首先,将数组中的所有数字进行异或运算,得到的结果就是只出现一次两个数字异或结果。因为其他数字出现两次,所以异或结果中的每一位,要么是两个出现一次数字对应位上的数字不同,要么是相同的数字中的某个数字在这一位上是0,另一个数字在这一位上是1。 然后,我们从异或结果中找到任意一位为1的位,即找到两个出现一次数字在这一位上不同的那个位。因为这两个数字在这一位上不同,所以我们可以根据这一位将原数组中的数字分成组,一组数字在这一位上是1,另一组数字在这一位上是0。那么这两个出现一次数字就被分到了不同的组中,其他数字都恰好被分成了一对对的数字,这就回到了“找出一个数字出现一次,其他数字出现两次”的问题,可以直接用异或的方法求解。 以下是Java代码实现: public int[] findNumbersAppearOnce(int[] nums) { if (nums == null || nums.length < 2) { return null; } // 将所有数字进行异或运算,找到两个出现一次数字异或结果 int xor = 0; for (int num : nums) { xor ^= num; } // 找到两个出现一次数字在哪一位上不同 int index = findFirstBitIs1(xor); // 按照这一位是否是1,将数组中的数字分成组 int num1 = 0, num2 = 0; for (int num : nums) { if (isBit1(num, index)) { num1 ^= num; } else { num2 ^= num; } } return new int[]{num1, num2}; } // 找到一个数字二进制表示中最右边是1的位 private int findFirstBitIs1(int num) { int index = 0; while ((num & 1) == 0 && index < 32) { num >>>= 1; index++; } return index; } // 判断一个数字二进制表示中从右往左数第index位是不是1 private boolean isBit1(int num, int index) { num >>>= index; return (num & 1) == 1; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值