位运算的奇巧淫技

下面主要介绍利用二进制的位运算解决问题,二进制和其他进制之间的一些以及二进制间的一些规律和技巧运用。

一、二进制中1的个数

问题如下:输入一个整数,输出该数二进制表示中1的个数
例如:9的二进制是1001, 有两个1。
解法一:最简单的方法是用一个1进行不断的移位,然后和输入的数字进行与运算,如果结果为0就表示该数的二进制表示在这个位置上为0.否则为1(所以得的结果和1移位后的数相等).
例如1001和1进行与运算结果为1,证明第一位为1(从右向左数)。将1左移2位得到10然后和1001进行与运算得到0,所以第二位为0. 因为一个整数是32位,所以只需要循环32次。代码如下:

package 位运算的奇巧淫技;
import java.util.*;

public class 二进制中1的个数解法一 {
	public static  void main(String [] args){
		Scanner scanner = new Scanner(System.in);
		int n = scanner.nextInt();
		//将n的二进制字符串表示给打印出来
		System.out.println(Integer.toString(n, 2));
		int count = 0;
		for(int i=0; i<32; i++){
			if((n&(1<<i))!=0) count++;
		}
		System.out.println(count);
	}
}

解法二:和解法一类似。1不动,将输入的整数进行右移然后和1进行与运算。代码省略,将上面解法一if语句修改为如下即可:

if(((n>>i)&1)!=0) count++;

解法三:这个方法用到二进制运算的一个规律:(x-1)& x 会消去x最低位的那一个1. 也就是说X减一和X进行与运算会消去X最低位的那一个1.如下图。利用这个规律我们就可以轻松的解决这个问题,也就是说如果一个数的二进制表示有三个1,就需要进行三次运算得到0.我们只需要统计做了几次这样的运算就可以得出这个二进制有几个1.
在这里插入图片描述

代码如下:

package 位运算的奇巧淫技;

import java.util.*;

public class 二进制中1的个数解法三 {
	public static void main(String [] args){
		Scanner scanner = new Scanner(System.in);
		int n = scanner.nextInt();
		System.out.println(Integer.toString(n, 2));
		int count = 0;
		while(n!=0){
			n = (n-1)&n;
			count++;
		}
		System.out.println(count);
	}
}

二、用一条语句判断一个整数是不是2的整数次方

解题思路:这题用到一个规律:一个整数是2的整数次方,那么它的二进制表示中就只有一个1.注意:1是2的零次方。也就是将问题转换为判断一个整数的二进制表示是否只有一个1;而且只用一条语句。对于这个问题我们用上题的规律可以轻松的解决:(x-1)& x 会消去x最低位的那一个1。进行这个运算后如果结果为零,就表示这个二进制只有一个1.
代码如下:

package 位运算的奇巧淫技;

import java.util.Scanner;

public class 判断整数是不是2的整数次方 {
	public static void main(String [] args){
		Scanner scanner = new Scanner(System.in);
		int n = scanner.nextInt();
		System.out.println(Integer.toString(n, 2));
		if(((n-1)&n)==0)
			System.out.println("是2的整数次方");
		else
			System.out.println("不是2的整数次方");
	}
}

三、将二进制整数的奇偶位互换

例如:1001的奇偶位进行互换得到0110.

解题思路:分别保留偶数位和奇数位得到C和D,然后将C左移一位,将D右移一位;然后将C和D进行异或运算就可以得到奇偶位互换后的结果。
保留偶数位的方法如下:与偶数位为1奇数位为0的二进制进行与运算
保留奇数位的方法如下:与奇数位为1偶数位为0的二进制进行与运算
在这里插入图片描述
代码如下:

package 位运算的奇巧淫技;

import java.util.Scanner;

public class 奇偶位互换 {
	public static void main(String [] args){
		Scanner scanner = new Scanner(System.in);
		int n = scanner.nextInt();
		System.out.println(Integer.toString(n, 2));
		int ou = n&0xaaaaaaaa;
		//用十六进制表示,a表示1010,因为整数是32位所以要8个a
		int ji = n&0x55555555;
		//5表示0101
		System.out.println((ji<<1)^(ou>>1));
		System.out.println(Integer.toString((ji<<1)^(ou>>1), 2));
	}
}

四、数组中只有一个数出现了一次,其他的数都出现了k次(k大于1),k是一个常数,请输出只出现了一次的数。

假设本题k等于3,即其他数都出现了三次。

解题思路:暴力法就不说了,这题用位运算解决要用到一个规律: N个相同的N进制进行不进位的加法结果为0.比如:两个相同的二进制进行不进位的加法(相当于异或运算)结果为0. 有了这个规律我们可以将数组的数都转换为是三进制,然后进行相加。最终结果就会得到那个只出现一次的数的三进制表示。把这个三进制转换为十进制就行了。

代码如下:

package 位运算的奇巧淫技;

import java.util.Arrays;
import java.util.Scanner;

public class 只出现了一次的数 {
	public static void main(String [] args){
		int[] a = {2,2,2,9,7,7,7,3,3,3,6,6,6,0,0,0};
		int len = a.length;
		char[][] s = new char[len][];
		int maxlen = 0;  //用统计每一行的最大长度
		for(int i=0; i<len; i++){//转换为三进制
			s[i] = new StringBuffer(Integer.toString(a[i], 3)).reverse().toString().toCharArray();
			//将字符串逆转是为了下面更好计算
			if(s[i].length > maxlen) maxlen = s[i].length;
		}
		int[] res = new int[maxlen];
		for(int i=0; i<len; i++){//不进位的加法
			for(int j=0; j<s[i].length; j++){
				res[j] = res[j] + (s[i][j] - '0');
				res[j] = res[j]%3;
			}
		}
		int sum = 0;
		for(int i=0; i<maxlen; i++){//将三进制转换为十进制
			sum += res[i]*Math.pow(3, i);
		}
		System.out.println(sum);
	}
}

上面提到的算法和技巧参考书籍如下:《程序员面试金典》《挑战程序设计竞赛》《程序员代码面试指南》《剑指offer》《编程之美》《算法导论》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥自在”

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值