视频链接:《算法很美》
1.位运算
位运算符
- 在处理整形数值时,可以直接对组成整形数值的各个二进制位进行操作
&(与)、|(或)、^(异或)、~(非/取反)>>和<<运算符将二进制位进行右移或者左移操作>>>运算符将用0填充高位;>>运算符用符号位填充高位,没有<<<运算符- 对于int型,
1<<35与1<<3是相同的,而左边的操作数是long型时需对右侧操作数模64 - 与:都为1结果为1,或:有一个为1结果为1,异或:二者不同时结果为1
| a | b | ~a | a&b | a|b | a^b |
|---|---|---|---|---|---|
| 1 | 1 | 0 | 1 | 1 | 0 |
| 0 | 1 | 1 | 0 | 1 | 1 |
| 0 | 0 | 1 | 0 | 0 | 0 |
位运算符的一些常见用法
-
1.判断奇偶数:
- x&1=1为奇数 =0为偶数 原因:奇数最后一位是1,&1后为1,偶数最后一位为0,&1后为0
-
2.判断二进制某个位上是1还是0 (两种解决方案)
- eg:86的二进制第5位是1还是0?
- 1先左移4位和86与运算,结果再右移,和为1则为1,为0则为0
- 86右移4位,然后结果和1与运算,结果1则为,为0则为0
86>>4&1
-
3.交换两个整数变量的值
int a=2,b=1;
a=a^b;
b=a^b;
a=a^b;
System.out.println("a=="+a+",b=="+b);
- 4.不用判断语句,求整数的绝对值
int a=-88;
System.out.println((a^a>>31)+(a>>>31));
// 结果:88
异或
第1题:找出唯一称对的数
1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空间,能否设计一个算法实现?
解题思路:
答案如下:
public class Test01 {
public static void main(String[] args) {
// 这里为了演示方便,数组长度为11
int N = 11;
int[] arr = new int[N];
for (int i = 0; i < arr.length - 1; i++) {
arr[i] = i + 1;
}
arr[N - 1] = new Random().nextInt(N - 1) + 1;
int index = new Random().nextInt(N);
int t;
t = arr[N - 1];
arr[N - 1] = arr[index];
arr[index] = t;
// 打印这个数组
System.out.println(Arrays.toString(arr));
// 1.位运算方法:异或
int x = 0;
// 1^2...^x^x..^10
for (int i = 0; i < N; i++) {
x = x ^ arr[i];
}
// (1^2...^x^x..^10)^(1^2...^x...^10)
for (int i = 1; i <= N - 1; i++) {
x = (x ^ i);
}
// x^x^x = x =>得到结果x
System.out.println(x);
// 2.辅助空间方法
int[] h = new int[N];
for (int i = 0; i < N; i++) {
h[arr[i]]++;
}
for (int i = 0; i < N; i++) {
if (h[i] == 2) {
System.out.println(i);
}
}
// 3.哈希表法
}
}
第2题:找出落单的那个数
一个数组里除了某一个数字之外,其他的数字都出现了两次。请写程序找出这个只出现一次的数字。
解题思路:
这道题思路简单很多,只需要用到A ^ A =0和A ^ 0 = A即可!
答案如下:
public class Test02 {
public static void main(String[] args) {
// 假设数组数据如下:求数组中的唯一不重复数据(4)
int[] arr = new int[] { 1, 1, 3, 3, 4, 5, 5, 7, 7, 9, 9 };
int x = 0;
for (int i = 0; i < arr.length; i++) {
x = x ^ arr[i];
}
System.out.println("数组arr中的唯一不重复元素是:" + x);// 4
}
}
第3题:二进制中1的个数
请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。
例: 9的二进制表示为1001,有2位是1
解题思路:
答案如下:
public class Test03 {
public static void main(String[] args) {
System.out.println("请输入一个数:");
Scanner scanner = new Scanner(System.in);
int N = scanner.nextInt();
String binN = Integer.toString(N,2);
System.out.println("您输入的数字:"+N+" 对应的二进制为:"+binN);
// 二进制中1的个数
int count = 0;
for (int i = 0; i < binN.length(); i++) {
if ((N&1<<i)== 1<<i) {
count++;
}
}
System.out.println("该二进制中1的个数为:"+count);
}
}
结果如图:
第4题:判断一个数是否是2的n次方
用一条语句判断一个整数是不是2的整数次方。
解题思路:
性质:一个数如果是2的整数次方,则该数的二进制中只有一个1:
- 2的0次方:1 => 1
- 2的1次方:2 => 10
- 2的2次方:4 => 100
- 2的3次方:8 => 1000
- …
所以我们只需要判断某个数的二进制是否满足上述性质,即可判断该数是不是2的整数次方。
答案如下:
public class Test04 {
public static void main(String[] args) {
System.out.println("请输入一个数:");
Scanner scanner = new Scanner(System.in);
int N = scanner.nextInt();
String binN = Integer.toString(N, 2);
System.out.println("您输入的数字:" + N + " 对应的二进制为:" + binN);
// 二进制中1的个数
int count = 0;
for (int i = 0; i < binN.length(); i++) {
if ((N & 1 << i) == 1 << i) {
count++;
}
}
System.out.println("该二进制中1的个数为:" + count);
System.out.println("该数是否是2的整数次方:"+ (count==1));
}
}
结果如图:
第5题:将整数的二进制奇偶位互换
将整数的二进制奇偶位互换,然后得到交换后的整数值
解题思路:
答案如下:
public class Test05 {
public static void main(String[] args) {
// 如果不采用位运算,可以采用整数转二进制字符串,然后对其进行分割成char数组,然后遍历数组并交换奇偶位
// 这里我们采用位运算的方式:
int N = 99;
System.out.println("99的二进制为:" + Integer.toString(N, 2));
int result = swap(N);
System.out.println("二进制奇偶位交换后的结果为:" + result);
System.out.println("result的二进制为:" + Integer.toString(result, 2));
}
private static int swap(int n) {
// 每4位二进制可以用1位十六进制数表示
int ou = n & 0xaaaaaaaa;// 和1010 1010 ...做与运算得到偶位
int ji = n & 0x55555555;// 和0101 0101 ...做与运算得到奇位
return (ou >> 1) ^ (ji << 1);
}
}
第6题:0~1间浮点实数的二进制表示
- 给定一个介于0和1之间的实数, (如0.625) ,类型为double,打印它的二进制表示(0.101,因为小数点后的二进制分别表示0.5,0.25.0.12…)。
- 如果该数字无法精确地用32位以内的-进制表示,则打印“ERROR”
解题思路:
答案如下:
public class Test06 {
public static void main(String[] args) {
double n = 0.625;
//double n = 0.3;
StringBuilder builder = new StringBuilder("0.");
while (n > 0) {
n = n * 2;
if (n >= 1) {
n = n - 1;
builder.append("1");
} else {
builder.append("0");
}
}
if (builder.toString().length()>34) {
System.out.println("ERROR");
return;
}
System.out.println(builder.toString());
}
}
第7题:出现k次与出现1次的数
数组中只有一个数出现了1次,其他的数都出现了k次,请输出只出现了1次的数。
注:如果不考虑位运算不考虑效率的话,用哈希表即可解决该题
解题思路:
- 2 个相同的2进制数做不进位加法,结果为0
- 10个相同的10进制数做不进位加法,结果为0
- k 个相同的k进制数做不进位加法,结果为0
答案如下:
public class Test07 {
public static void main(String[] args) {
int[] arr = {2, 2, 2, 9, 7, 7, 7, 3, 3, 3, 6, 6, 6, 0, 0, 0};
int len = arr.length;
char[][] kRadix = new char[len][];
int k = 3;
int maxLen = 0;
//转成k进制字符数组
//对于每个数字
for (int i = 0; i < len; i++) {
//求每个数字的三进制字符串并翻转,然后转为字符数组
kRadix[i] = new StringBuilder(Integer.toString(arr[i], k)).reverse().toString().toCharArray();
if (kRadix[i].length > maxLen)
maxLen = kRadix[i].length;
}
//不进位加法
int[] resArr = new int[maxLen];
for (int i = 0; i < len; i++) {
// 不进位加法
for (int j = 0; j < maxLen; j++) {
if (j >= kRadix[i].length)
resArr[j] += 0;
else
resArr[j] += (kRadix[i][j] - '0');
}
}
int res = 0;
for (int i = 0; i < maxLen; i++) {
res += (resArr[i] % k) * (int) (Math.pow(k, i));// 8%3=2,
}
System.out.println(res);
}
}