算法很美1.2 用位运算巧解

目录

1.找出数组当中唯一重复[成对]的那个数

2.二进制数中1的个数

3.判断一个数是否为2的整数次方

 4.将整数的二进制数的奇偶位进行互换

5.用二进制表示浮点实数

 6.在数组当中只有一个数字出现了1次,其余均出现了K次,请找出出现1次的数字


1.找出数组当中唯一重复[成对]的那个数

        

知识回顾:根据上篇位运算的基础,可以知道:

附上篇位运算基础链接:算法很美1.1

  1. 任何数与0异或等于其本身【A^0=A】
  2. 任何数与其本身异或等于0【A^A=0】 

思路:x又由本题可知:只有一个数据重复,我们可以额外获得一个式子x其为:1^2^3^4~^1000;

再将此式与数组当中每一数字进行异或,因为只有一个数字重复,所以除了该重复数据,与式子x异或后未被消去,其余都根据任何数与其本身异或等于0【A^A=0】 得到了消除,这样我们就得到了唯一重复的数字。

举例:假设数组有4个元素,重复数据为3

则式子x=1^2^3

式子x与数组每一个数字进行异或:1^1^2^2^3^3^3=3

代码:如下

public class Main {
	public static void main(String[] args) {
		//对数组进行赋值
		int[] x=new int[1001];
		for (int i = 0; i < 1000; i++) 
			x[i]=i+1;
		//产生随机数
		x[1000]=(int)(Math.random()*(1000-0+1)+0);
		System.out.println("重复的数字为:"+x[1000]);
		//法一:位运算法  [不开辟额外的辅助空间]
		int a=0;
		for (int i = 1; i <=1000; i++) 
			a=a^i;
		for (int i = 0; i < x.length; i++) 
			a=a^x[i];	
		System.out.println("位运算法:"+a);
		
		//法二  [暴力]
		int[] count=new int[1000];
		for (int i = 0; i < x.length; i++) 
			count[x[i]-1]++;
		for (int i = 0; i < count.length; i++) 
			if(count[i]==2) 
				System.out.println("暴力:"+a);

	}
}

补充:除了位运算我们当然还可以用暴力求解,即违背了题目要求 开辟额外的存储空间,进行遍历要求解的数组,在新数组当中存储其出现的次数来进行计数区分。代码也一并给出如上。

小结:本题位运算法适合已经知道数的范围

2.二进制数中1的个数

知识回顾:int共32位

思路:

        法1、法2:

                将每一位进行 与1 若结果为1,则count++,来统计1的个数

        法3:

               x=(x-1)&x    可以消去最低位的1,将x中的1全部消去,直到为0为止。每消去1次,count++。

 法3举例:例如10的二进制数为1010,1010-1=1000,将1000与1010相与即可消去最低位的的1,count++,再将1000-1=0111,将0111与1000相与得到0000,count++,此时为0停止操作

代码:如下

public class Main {
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int x=sc.nextInt();
		System.out.println(Integer.toString(x, 2));
		int count = 0;//1的个数
		//法1
		for (int i = 0; i <32 ; i++) {
			if((x&(1<<i))==(1<<i))
				count++;
		}
		count=0;
		//法2
		for (int i = 0; i <32 ; i++) {
			if(((x>>i)&1)==1)
				count++;
		}
		count=0;
		//********法3**********
		while(x!=0) {
			x=(x-1)&x;
			count++;
		}
		System.out.println(count);
	}
}

小结:重点关注法3.    x=(x-1)&x    可以消去最低位的1

3.判断一个数是否为2的整数次方

知识回顾:判断一个数是否为2的整数次方即:该数字的二进数只含有一个1

思路:根据2题的法三,只需判断该数的二进数是否只含有一个1即可

代码:如下

public class Main {
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int x=sc.nextInt();
		int q=x;
		System.out.println(Integer.toString(x, 2));//显示输入数的2进制
		int count = 0;//1的个数
		while(x!=0) {
			x=(x-1)&x;
			count++;
		}
		if(count==1) 
			System.out.println(q+"是2的整数次方");
		else
			System.out.println(q+"不是2的整数次方");
	}
}

 4.将整数的二进制数的奇偶位进行互换

知识回顾:0x为十六进制的前缀标识,0位八进制的前缀标识,十进制没有前缀标识。       

                  int类型的整数有32位,而a在16进制当中表示为十进制的10,十进制的10在二进制当中表示为1010,占4位 所以可以用 0xaaaaaaaa表示32位的1010_1010_1010……

思路:规定从二进制数右起为第1位

        1.获得只保留奇数位上的整数:int类型的整数有32位,将其与0x55555555,即:0101_0101_0101……相与即可保留偶数位上的数字,与0相与消去,与1相与等于本身。

        2.获得只保留偶数位上的整数:同理与0xaaaaaaaa,即:1010_1010_1010……相与即可保留奇数位上的数字。

        3.实现交换:再将得到的奇数位的结果向左移一位,偶数位的结果相右移一位,将两者相或,即可实现交换。

 法3举例:13二进制数为1101  先获取只保留奇数位的数字:与0x55555555相与得到0101.再向右移一位得到1010。获取只保留偶数位的数字:与0xaaaaaaaa相与得到1000.再向左移一位得到0100。两者相或即可得到:1110

代码:如下

public class Main {
	public static void main(String[] args) {
//		0x为十六进制的前缀标识,0位八进制的前缀标识,十进制没有前缀标识。
		Scanner sc=new Scanner(System.in);
		int x=13;
		System.out.println(Integer.toString(x, 2));//显示输入数的2进制
		int j=x&0x55555555;//0101_0101_0101……
		int o=x&0xaaaaaaaa;//1010_1010_1010……
		int y=(o>>1|j<<1);
		System.out.println(Integer.toString(y, 2));
	}
}

5.用二进制表示浮点实数

 

知识回顾:此处涉及到了二进制与十进制之间的转换,其分为十进制整数部分转换为二进制和十进制小数部分转换为二进制。详情请见:十进制转换为二进制

思路:我们只需按照十进制小数部分转换为二进制的规则来进行编写代码即可

代码:如下

public class Main {
	public static void main(String[] args) {
	double num=0.3;
	StringBuffer str=new StringBuffer("0.");
	while(num>0) {
		double x=num*2;
		if(x>=1) {
			num=x-1;
			str.append("1");
		}else {
			str.append("0");
			num=x;
		}
		//长度超过了32位   因为我们将str的初始化设置了为"0."占用两个长度
		//所以这里是>34
		if(str.length()>34) {
			System.out.println("ERRO");
			return;
		}
	}
		System.out.println(str);
	}
}

 6.在数组当中只有一个数字出现了1次,其余均出现了K次,请找出出现1次的数字

知识回顾:1.k相同的k进制数做不进位加法,结果为0

                  2.进制转换:Integer.toString(i, radix)

思路:其余数字均出现了k次,则先把数组当中所有数字转换为k进制,对所有转换为k进制的数字做不进位加法,因为k个相同的k进制数做不进位加法,结果为0,得到的最后结果只含有出现一次的k进制的数字,再将其再转换为十进制数即可求解。

举例:2个2进制数:01+01 做不进位加法的结果为00

代码:如下

public class Main {
	public static void main(String[] args) {
	//本题以k=4为例进行说明,只有6出现了一次其余均出现了4次
	//所以要将其转换为4进制
	int[] num=new int[] {3,3,3,3,8,8,8,8,6,12,12,12,12};
	//定义一个二维的字符数组,在里面存放对应num数组转换为k进制的字符
	char [][] cj=new char[num.length][];
	int maxlen=0;
		/*
		 这里是将num数组每一个数字转换为k进制
		 然后将其对应的k进值字符反转后加入cj二维数组当中
		 
		 *******反转的原因:
		 为了保证cj存放的k进制数字的字符对应位的等级一致
		 */
	for (int i = 0; i < cj.length; i++) {
		
		cj[i]=new StringBuffer(Integer.toString(num[i], 4)).reverse().toString().toCharArray();
		//	获取最大的字符长度
		if(cj[i].length>maxlen)
			maxlen=cj[i].length;
	}
//	此数组存放的是不进位加法后得到的只存放一次数组的k进制的数值
	int[] answer=new int[maxlen];
	//不进位加法
	for (int i = 0; i < num.length; i++) {
		for (int j = 0; j < maxlen; j++) {
			if(j>=cj[i].length) {
				answer[j]+=0;
			}else {
				answer[j]+=(cj[i][j]-'0');
			}
		}
	}
	
	int a=0;//最终结果
	for (int i = 0; i < answer.length; i++) {
		a+= (answer[i]%4)*(int)Math.pow(4, i);
	}
	System.out.println(a);
	}
}

 注意点:将所有对应位的加法结果进行取余k,即相当于得到了不进位的加法结果

例如:两个二进制01   最低位进行不进位加法:(1+1)%2=0[即不进位的结果]

此处代码如下:

for (int i = 0; i < answer.length; i++) {
		a+= (answer[i]%4)*(int)Math.pow(4, i);
	}

特别注意该题还可以用哈希表来解决更为高效,此处只是体会学习位运算的使用,而非最优解。只做了解与体会位运算的运用即可。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值