android 异或类型,谈谈面试中的那些异或操作

最近一直在面试,也做了各种各样的手写算法题,大部分时候面试官想要考察的只是候选人对常见算法的了解程度。有些题很难,通过一些骚操作可以达到更高的性能,比如最长回文子串问题的最优解是马拉车算法,但是那些算法太偏门了,需要深厚的理论基础,我们不是专门做算法的,可能面试官自己也都不会,他出这道题一般是想你用动态规划来解。(当然了,你就用马拉车算法来做肯定会让面试官眼前一亮,留下深刻的印象)还有一种情况就是题目很简单,简单的一两个加减乘除都能做出来,这时候面试官想考察的肯定不是你会不会做算术,这时候一般都是考察候选人位运算玩的溜不溜。

我这两天就遇到这样的问题,今天就主要来谈谈异或运算在面试中的考察方式。

首先来看一道题:在一个非空整数数组中,除了一个数其它数都出现了两次,找出这个数。找出某个元素出现的次数,这个概念我们很熟悉呀,用哈希表就行了嘛!做一个循环,第一次遇到一个数的时候把它添加进哈希表,第二次遇到的时候移除,最后表里剩下来的那个就是我们要找的数。但是这样时间复杂度跟空间复杂度都在O(n),而且这么做太简单了,怎么办,还有没有其它解法?

我们回想一下异或运算符的特性,两个操作数相同的话为0,任何数与0做异或的结果还是那个数。这样我们可以对数组里面的所有元素做异或操作,相同的两个数都会变成0,剩下的那个数跟0做异或结果还是那个数,最后我们就能得到我们的结果啦:

public static int findSingleNumber(int[] arr) {

int num = 0;

for (int i = 0; i < arr.length; i++) {

num = num ^ arr[i];

}

return num;

}

复制代码

这时候时间复杂度还是O(n),但是空间复杂度已经达到O(1)喽。

现在我们把难度升级一下,数组里存在两个唯一的数,我们该怎么把它找出来呢?还是按照之前的方法的话,我们只能得到n1^n2,拿不到独立的数的呀?难道还是哈希表大法好?

别急,我们知道n1跟n2是不同的数,那么在它们二进制的表示上,至少有一位是不一样的,那我们可以跟这一位上是1还是0把数组里的数分成两拨,这两个数肯定就在各自的分组里,然后相同的数也肯定在同一个分组里,再让这两拨数做异或运算,最后各自剩下来的不就是我们要的两个数了吗?

那么怎么找到这不一样的位置呢?还记得位运算的特性,0跟1做运算下来还是1,我们从右向左依次跟1做与运算,就能找到它了。

public static int[] findSingleNumbers(int[] nums) {

// 得到做完异或操作之后的结果

int n1xn2 = 0;

for (int num : nums) {

n1xn2 ^= num;

}

// 得到从右往左第一个是1的位

int rightmostSetBit = 1;

while ((rightmostSetBit & n1xn2) == 0) {

rightmostSetBit = rightmostSetBit << 1;

}

int num1 = 0, num2 = 0;

for (int num : nums) {

if ((num & rightmostSetBit) != 0) // 一拨

num1 ^= num;

else // 另一拨

num2 ^= num;

}

return new int[]{num1, num2};

}

复制代码

通过与运算,我们成功把数组分为两拨,然后再通过异或操作,得到我们的结果。

最后再来看看我遇到的这道题:每个非负整数 N 都有其二进制表示。例如, 5 可以被表示为二进制 "101",11 可以用二进制 "1011" 表示,依此类推。注意,除 N = 0 外,任何二进制表示中都不含前导零。二进制的反码表示是将每个 1 改为 0 且每个 0 变为 1。例如,二进制数 "101" 的二进制反码为 "010"。给你一个十进制数 N,请你返回其二进制表示的反码所对应的十进制整数。

我们再来回顾一下异或运算的特性:

1^0=0^1=1

0^0=1^1=0

任何数跟0异或都不变

从第一点我们可以知道一个数跟它的反码异或会得到一个各位都是1的数。我们就可以写出下面的公式:

number ^ complement = all_bits_set

复制代码

那么:

number ^ number ^ complement = number ^ all_bits_set

复制代码

然后:

0 ^ complement = number ^ all_bits_set

复制代码

最终:

complement = number ^ all_bits_set

复制代码

我们可以基于这条公式去求我们想要的结果了。现在的任务就变成如何获取这个all_bits_set了,也很简单,知道这个数占几位就好了。

public static int bitwiseComplement(int num) {

// 计算位数

int bitCount = 0;

int n = num;

while (n > 0) {

bitCount++;

n = n >> 1;

}

int all_bits_set = (int) Math.pow(2, bitCount) - 1;

// 根据我们之前推导的公式

return num ^ all_bits_set;

}

复制代码

时间复杂度根据位数决定,空间复杂度维持在O(1)。

总而言之,这类的题型其实很固定,一堆数里找特定的数啊,一个数的特定变形啊,我们只要关注异或运算那三种特性,那解题就没有太大障碍了。

b739ec46bb5c46d9c0aa4ce35ba1ea56.png

关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。

本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。

[谈谈面试中的那些异或操作]http://www.zyiz.net/tech/detail-137550.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值