- 判断一个数是否是偶数
/**
* 如果把 n 以二进制的形式展示的话,其实我们只需要判断最后一个二进制位是 1 还是 0 就行了,
* 如果是 1 的话,代表是奇数,如果是 0 则代表是偶数,
* 所以采用位运算的方式的话,如下
* @param n
* @return
*/
private static boolean isOddNumber(int n) {
return (n & 1) == 1;
}
2.交换两个数
不允许你使用额外的辅助变量来完成交换呢
/**
* 三个都是 x ^ y,就莫名交换成功了。在此我解释下吧,我们知道,
* 两个相同的数异或之后结果会等于 0,
* 即 n ^ n = 0。并且任何数与 0 异或等于它本身,即 n ^ 0 = n。所以,解释如下:
把(1)中的 x 带入 (2)中的 x,有
y = x^y = (x^y)^y = x^(y^y) = x^0 = x。 x 的值成功赋给了 y。
对于(3),推导如下:
x = x^y = (x^y)^x = (x^x)^y = 0^y = y。
异或运算支持运算的交换律和结合律
*/
private static void exchange() {
int x = 10;
int y = 20;
System.out.println("before exchange: ");
System.out.println("x = "+x+", y = "+ y);
x = x ^ y;
y = x ^ y;
x = x ^ y;
System.out.println("after exchange: ");
System.out.println("x = "+x+", y = "+ y);
}
- 给你一组整型数据,
这些数据中,其中有一个数只出现了一次,其他的数都出现了两次,
让你来找出一个数 。
/**
* 我们刚才说过,两个相同的数异或的结果是 0,一个数和 0 异或的结果是它本身,所以我们把这一组整型全部异或一下,
* 例如这组数据是:1, 2, 3, 4, 5, 1, 2, 3, 4。其中 5 只出现了一次,
* 其他都出现了两次,把他们全部异或一下,结果如下:
由于异或支持交换律和结合律,所以:
1^2^3^4^5^1^2^3^4 = (1^1)^(2^2)^(3^3)^(4^4)^5= 0^0^0^0^5 = 5。
也就是说,那些出现了两次的数异或之后会变成0,那个出现一次的数,和 0 异或之后就等于它本身
* @param arr
* @return
*/
private static int find(int[] arr) {
int temp = arr[0];
for (int i = 1; i < arr.length; i++) {
temp = temp ^ arr[i];
}
return temp;
}
4.m的n次方
/**
* 计算 m 的 n 次方
* 我举个例子吧,例如 n = 13,则 n 的二进制表示为 1101,
* 那么 m 的 13 次方可以拆解为:
m^1101 = m^0001 * m^0100 * m^1000。
我们可以通过 & 1和 >>1 来逐位读取 1101,为1时将该位代表的乘数累乘到最终结果。
时间复杂度近为 O(logn),而且看起来很牛逼。
* @param m
* @param n
* @return
*/
private static int pow(int m, int n) {
int sum = 1;
int tmp = m;
while(n != 0) {
if((n & 1) == 1) {
sum *= tmp;
}
tmp *= tmp;
n = n >> 1;
}
return sum;
}
5.找出不大于N的最大的2的幂指数
/**
* 找出不大于N的最大的2的幂指数
* 例如 N = 19,那么转换成二进制就是 00010011
* (这里为了方便,我采用8位的二进制来表示)。那么我们要找的数就是,
* 把二进制中最左边的 1 保留,后面的 1 全部变为 0。即我们的目标数是 00010000。
* 那么如何获得这个数呢?相应解法如下:
*
1、找到最左边的 1,然后把它右边的所有 0 变成 1
2、把得到的数值加 1,可以得到
00100000即 00011111 + 1 = 00100000。
3、把 得到的 00100000 向右移动一位,
即可得到 00010000,即 00100000 >> 1 = 00010000。
那么问题来了,第一步中把最左边 1 中后面的 0 转化为 1 该怎么弄呢?我先给出代码再解释吧。
下面这段代码就可以把最左边 1 中后面的 0 全部转化为 1,
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
就是通过把 n 右移并且做或运算即可得到。我解释下吧,
我们假设最左边的 1 处于二进制位中的第 k 位(从左往右数),
那么把 n 右移一位之后,那么得到的结果中第 k+1 位也必定为 1,
然后把 n 与右移后的结果做或运算,那么得到的结果中第 k 和 第 k + 1 位必定是 1;
同样的道理,再次把 n 右移两位,那么得到的结果中第 k+2和第 k+3 位必定是 1,
然后再次做或运算,那么就能得到第 k, k+1, k+2, k+3 都是 1,如此往复下去….
这种做法的时间复杂度近似 O(1),重点是,高逼格。
* @param n
* @return
*/
private static int findN(int n) {
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8; // 整型一般是 32 位,上面我是假设 8 位
// 这种方式有可能导致溢出
// return (n + 1) >> 1;
// 下面这种方式可以避免溢出的风险
return n - (n >>> 1);
}
6.判断一个数是否是2的N次方
public static boolean is2ToTheNthPower(int value) {
// 如果输入的数字是2的N次方会等于0
// 举个例子 8:
// 8的二进制数: 0000 1000
// 8-1 的二进制数: 0000 0111
// &
// 8 & (8-1) = 0000 0000
return (value & (value - 1)) == 0;
}
7.获取最右边的1
public static int getRightOne(int value) {
// 提取出最右边的1
int rightOne = value& (~value+ 1);
// 假设 value : 0010 1000
// 取反 value : 1101 0111
// 加1 1
// 1101 1000
// & value: 0010 1000
// 0000 1000 得到最右的1的值
return rightOne;
}
8.arr中,有两种数 a 和 b 出现奇数次 , 其他数均出现偶数次 ,找出出现奇数次的两个数 a 和 b 是多少?
public static void printOddTimesNum2(int[] arr) {
int eor = 0;
for (int i = 0; i < arr.length; i++) {
eor ^= arr[i];
}
// eor = a ^ b
// eor != 0
// eor 必有一个位置是1
// 提取出最右边的1
int rightOne = eor & (~eor + 1);
int onlyOne = 0;
// a 和 b 中必有其中一个数与 rightOne 在同一个位置上是1 ,以此区分出数组中的 a 与 b
for (int i = 0; i < arr.length; i++) {
if ((arr[i] & rightOne) != 0) {
onlyOne ^= arr[i];
}
}
// onlyOne 必定是 a 和 b 其中一个
// eor = a ^ b
System.out.println("a 和 b 分别是: "+onlyOne + " : "+(eor ^ onlyOne));
}
9.统计1的个数
public static int bitOneCounts(int N) {
int count = 0;
while (N != 0) {
count ++;
int rightOne = N & (~N + 1);
N ^= rightOne;
// 为什么不用 N -= rightOne , 如果保证N是正数可以 ,N 是负数的话就不行了, 异或最好
// 使用这种方法可以直接消除最右边的1
// N &= (N - 1);
}
return count;
}
测试代码:
public static void main(String[] args) {
// 1.
int n = 3;
if(isOddNumber(n)) {
System.out.println(n + " 是奇数");
}else {
System.out.println(n+" 不是奇数");
}
// 2.交换两个数
// 不允许你使用额外的辅助变量来完成交换呢
exchange();
// 3. 给你一组整型数据,
// 这些数据中,其中有一个数只出现了一次,其他的数都出现了两次,
// 让你来找出一个数 。
int[] arr = new int[] {1, 2,3,4, 5,1, 2,3,4};
int result = find(arr);
System.out.println("只出现一次的数: "+result);
// 4.m的n次方
result = pow(2,13);
System.out.println("2 的 13 次方是: "+result);
// 5.找出不大于N的最大的2的幂指数
int N = 19;
result = findN(N);
System.out.println("找出不大于19的最大的2的幂指数: "+result);
// 6.判断一个数是否是2的N次方
int input = 1024;
boolean b = is2ToTheNthPower(input);
String str = b?"是":"不是";
System.out.println("数字 "+ input +" " +str+" 2的N次方");
}
此编并非原创,只是当初在论坛上看某个大佬的文章,但是找不到链接了,所以使用了原创这个标签