蓝桥杯算法课《算法最美》笔记 ——1、位运算

1. 位运算基础

1.1.位运算符

&, |, ^, ~ 四个基本运算符

A&B:1001&1010 = 1000

异或: 如果a、b两个值不相同,则异或结果为1。. 如果a、b两个值相同,异或结果为0

异或运算(^)是以二进制数据为基础进行运算的。也就是说当代码中使用到异或运算时,都会先将两个条件进行转换,转换成二进制数据后,再进行运算。异域中同位如果值相同(都是0或者都是1)则为0,不同(一个是0,一个是1)为1。

将二进制右移(用符号位填充高位) >>

将二进制左移 <<

将二进制右移(用0填充高位) >>>

注意:无<<<

**例:1<<35 = 1<<3 **

System.out.println(1<<3);
System.out.println(1<<35);

输出8(1000)

1.1.1题一:找出唯一成对的数

题目:1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,其他均只出现一次。每个数组元素只能访问一次,设计一个算法,将他找出来;不用辅助存储空间,能否设计一个算法实现。

方法一:

思路:根据A ^ A ^ B ^ C ^ C = B,所以让数组中的元素全部进行异或运算,再与1-1000进行异或运算,其中出现两次重复的数据异或为0,出现三次的数据异或为它本身,然后打印出来,得到最终结果。

import java.util.Arrays;
import java.util.Random;

public class 如何找数组中唯一成对的那个数 {
	public static void main(String[] args) {
		int N = 1000;
		int[] arr = new int[N];
		//1-10放到数组中
		for (int i = 0; i < arr.length-1; i++) {
			arr[i]=i+1;
		}
		//随机产生一个随机数放入数组的最后位置
		Random random = new Random();
		arr[arr.length-1] = random.nextInt(N-1)+1;
		//打印数组内容
			System.out.println(Arrays.toString(arr));
		int x = 0 ;//0与任何数异或还是它本身
		//1    -   N-1进行异或
		for (int i = 0; i < N-1; i++) {
			x = x^(i+1);
		}
		for (int i = 0; i < N; i++) {
			x=x^arr[i];
		}
		System.out.println(x);
	}
}

方法二:

思路:用辅助空间,直接暴力破解。创建一个计数器数组helper,将数组arr中出现的数作为索引进行累加,出现两次的那个数在数组helper中对应的内容必为2,然后输出即可。

	/暴力破解法============================
		int[] helper = new int[N];
		for (int i = 0; i < N; i++) {
			helper[arr[i]]++;
		}
		for (int i = 0; i < N; i++) {
			if(helper[i] == 2) {
				System.out.println(i);
				break;
			}
		}

1.1.2题二:找出落单的那个数

题目:一个数组中里除了某一个数字之外,其他的数字都出现了两次。请写程序找出这个只出现一次的数字。

思路:直接根据题一把数组中的元素进行异或即可。具体代码见1.1.1

1.1.3题三:二进制中1的个数

题目:请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。

例:9的二进制表示为1001,二进制位数为2。

方法一:

思路:9=000…0001001, 将1进行左移位,移位32次, 就是进行32次循环,然后1<<i与9进行与运算,得到一个32位的数字,里面肯定只最多只有一个1, 然后让这个数字与1<<i判断是否相等, 相等则计数器加1.

import java.util.Scanner;

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;
		//比对每一位
		for (int i = 0; i < 32; i++) {
			if((N&(1<<i)) == (1<<i)) {
				count++;
			}
		}
		System.out.println(count);
	}
}

输出:

9
1001
2

方法二:

思路:将原数字不断往由移动,每次移动一个数字,步骤和方法一类似.

import java.util.Scanner;

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;
		//比对每一位
		for (int i = 0; i < 32; i++) {
			if(((N>>i)&1) == 1) {
				count++;
			}
		}
		System.out.println(count);
	}
}

方法三:

思路:原数字N每次减一,与N做与运算。1001-1=1000,1000&1001=1000,1000-1=0111,0111&1000=0000

做到第几次为0000,则运算结束。

import java.util.Scanner;

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);
	}
}

1.1.4题四:判断是不是2的整数次方

题目:用一条语句判断一个整数是不是2的整数次方。

思路:转化成二进制只有一个1

public class 判断是不是二的整数次方 {
	public static void main(String[] args) {
		int N = 9;
		if((N-1)&N) == 0) {
			System.out.println(N+"是2的整数次方");
		}else{
			System.out.println(N+"不是2的整数次方");
		}
	}
}

1.1.5题五:将整数的奇偶位交换

题目:将整数的奇偶位交换

思路:以9为例,9与…01010进行&运算,将9的偶数位与…01010的偶数位相同的置为1,其余置为0;(1)

​ 9在与…010101进行&预算,将9的奇数位与…01010的奇数位相同的置为1,其余置为0;(2)

​ 然后将(1)右移一位,将(2)左移一位,最后再进行异或运算得到最终结果。

public class 将整数的奇偶位交换 {
	public static void main(String[] args) {
		int N = 9;
		System.out.println(Integer.toString(N,2));
		int newN = change(N);
		System.out.println(Integer.toString(newN,2));
	}
	
	public static int change(int i) {
		int ou = i&0xaaaaaaaa ;
		int ji = i&0x55555555;
		return (ou>>1)^(ji<<1);
	}
}

输出:1001

​ 110

1.1.6 0~1间浮点实数的二进制表示

题目:给它定一个介于0和1之间的实数,如0.625,类型位double,打印他的二进制表示0.101;如果该数字无法精确的用32位以内的二进制表示,则打印 ERROR。

思路:将实数N×2,如果大于等于1,将1保留,如果小于1,将0保留。并判断字符串长度是否大于32位,最后输出最终结果。

public class 浮点数的二进制表示 {
	public static void main(String[] args) {
		StringBuilder sBuilder = new StringBuilder("0.");
		double N = 0.625;
		while(N>0) {
			double NN = N*2;
			if(NN >= 1) {
				sBuilder.append(1);
				N = NN-1;
			}
			else {
				sBuilder.append(0);
				N=NN;
			}
		}
	if(sBuilder.length() > 34) {
		System.out.println("Error");
	}
		System.out.println(sBuilder.toString());
	}
}

1.1.7 出现K次和出现一次

题目:数组中只有一个数出现了一次,其他的数都出现了K次,请输出只出现了一次的数。

思路:例如:2228777333,可以计数,但是可以用另一种方法:K个相同的K进制数做不进位加法,结果为0。然后可以把只出现了一次的数进行输出。这次感觉暴力更简单

public class 出现一次和出现K{
	public static void main(String[] args) {
		int[] arr = {2,2,2,7,7,7,8,9,9,9};
		int[] count=new int[arr.length];
		//直接暴力
		for (int i = 0; i < arr.length; i++) {
			//System.out.println(Integer.toString(arr[i],3));	
			for (int j = 0; j < arr.length; j++) {
				if(arr[i] == arr[j]) {
					count[i]++;
				}
			}
		}
		//找出计数数组中为1的数
		for (int i = 0; i < count.length; i++) {
			if(count[i] == 1) {
				System.out.println(arr[i]);
			}
		}
	}
}

输出:8

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大果壳Clap

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

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

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

打赏作者

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

抵扣说明:

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

余额充值