LeetCode刷题笔记(3)Java位运算符与使用按位异或(进制之间的转换)
1.问题描述
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
算法应该具有线性时间复杂度并且不使用额外空间。
输入: [4,1,2,1,2]
输出: 4
2.解题思路
这道题的主要的难点是具有线性时间复杂度并且不能使用额外的空间,因此就排除了很多的方法。
当时使用双指针尝试了以下,但是并没有取得成功,因为最后的结果可能是错误的。
3.正确解题思路
使用“按位异或”,即Java中的‘^’运算符来进行计算。
由于异或的原则是,不同为1,相同为0,题目中给定的数组中,如果两个相同的数异或的结果一定为0,最后得到的结果就是只出现一次的元素。
public int singleNumber(int[] nums) { int res = nums[0]; for (int i = 1; i < nums.length; i++) { res ^= nums[i]; } return res; }
4.另外的一个例子
需要找出t中不在s中的那个字符。
输入: s = "abcd" t = "abcde" 输出: e
也可以用按位异或的方式进行计算
public char findTheDifference(String s, String t) { char c = 0; for (int i = 0; i < s.length(); ++i) { c ^= s.charAt(i); } for (int i = 0; i < t.length(); ++i) { c ^= t.charAt(i); } return c; }
5.问题描述
不使用“+”“-”运算符计算两个整数的和。
(1)自己的思路:模拟计算机实际来操作二进制数补码的加法
Integer.parseInt无法将一个负数的补码转换成原始的负数,否则会报错java.lang.NumberFormatException
此时,只能这么来计算:取反码,然后加1,转换成相反数,然后添加上一个符号“-”
// System.out.println(new e371().getSum(a, b)); System.out.println("11111111111111111111111111101100".length()); System.out.println(Integer.toBinaryString(-20)); // System.out.println(Integer.parseInt("11111111111111111111111111101100", 2)); System.out.println(Integer.parseInt("00000000000000000000000000010100", 2));
解题思路:模仿实际计算机的真实计算结果,超级麻烦!!!!!!
class Solution { public int getSum(int a, int b) { String aStr = Integer.toBinaryString(a); String bStr = Integer.toBinaryString(b); String longStr = (aStr.length() < bStr.length()) ? bStr : aStr; String shortStr = (aStr.length() < bStr.length()) ? aStr : bStr; StringBuffer sb = new StringBuffer(); for (int i = 0; i < longStr.length() - shortStr.length(); i++) { sb.append(0); } shortStr = sb.toString().concat(shortStr); boolean isUp = false; StringBuffer resSB = new StringBuffer(); for (int i = longStr.length() - 1; i >= 0; i--) { if (!isUp) { if (longStr.charAt(i)== '1' && shortStr.charAt(i) == '1') { isUp = true; resSB.append(0); } else { resSB.append(Integer.valueOf(longStr.charAt(i)) ^ Integer.valueOf(shortStr.charAt(i))); } } else { if (longStr.charAt(i) == '1' && shortStr.charAt(i) == '1') { isUp = true; resSB.append(1); } else if (longStr.charAt(i) == '0' && shortStr.charAt(i)== '0') { resSB.append(1); isUp = false; } else { resSB.append(0); isUp = true; } } } if (isUp && resSB.length() < 32) resSB.append(1); String result = resSB.reverse().toString(); if (result.length() < 32) { return Integer.parseInt(result, 2); } if (result.charAt(0) == '0') { return Integer.parseInt(result, 2); } else { StringBuffer sbsb = new StringBuffer(); for (char c : result.toCharArray()) { if (c == '1') { sbsb.append(0); } else { sbsb.append(1); } } result = sbsb.toString(); longStr = result; shortStr = "00000000000000000000000000000001"; boolean isUp_1 = false; StringBuffer resSB_1 = new StringBuffer(); for (int i = longStr.length() - 1; i >= 0; i--) { if (!isUp_1) { if (longStr.charAt(i)== '1' && shortStr.charAt(i) == '1') { isUp_1 = true; resSB_1.append(0); } else { resSB_1.append(Integer.valueOf(longStr.charAt(i)) ^ Integer.valueOf(shortStr.charAt(i))); } } else { if (longStr.charAt(i) == '1' && shortStr.charAt(i) == '1') { isUp_1 = true; resSB_1.append(1); } else if (longStr.charAt(i) == '0' && shortStr.charAt(i)== '0') { resSB_1.append(1); isUp_1 = false; } else { resSB_1.append(0); isUp_1 = true; } } } result = resSB_1.reverse().toString(); return -Integer.parseInt(result, 2); } } }
(2)更好的思路,使用位运算符
public int getSum(int a, int b) { if(b == 0){ //没有进位的时候完成运算 return a; } int sum,carry; sum = a^b; //完成第一步加法的运算 carry = (a&b)<<1; //完成第二步进位并且左移运算 return getSum(sum,carry);// }
根据实际例子分析这块代码:
(1)a=1,b=2
a-> 00000000 00000000 00000000 00000001
b-> 00000000 00000000 00000000 00000010
b=2
sum-> 00000000 00000000 00000000 00000011
a&b-> 00000000 00000000 00000000 00000000
carry-> 00000000 00000000 00000000 00000000 输出sum=3
(2)a=1,b=7
a-> 00000000 00000000 00000000 00000001
b-> 00000000 00000000 00000000 00000111
a&b-> 00000000 00000000 00000000 00000001
sum-> 00000000 00000000 00000000 00000110
carry-> 00000000 00000000 00000000 00000010
a&b-> 00000000 00000000 00000000 00000010
sum-> 00000000 00000000 00000000 00000100
carry-> 00000000 00000000 00000000 00000100
a&b-> 00000000 00000000 00000000 00000100
sum-> 00000000 00000000 00000000 00000000
carry-> 00000000 00000000 00000000 00001000
a&b-> 00000000 00000000 00000000 00000000
sum-> 00000000 00000000 00000000 00001000
carry-> 00000000 00000000 00000000 00000000 输出sum = 8
(3)a=-16,b=14
a-> 11111111111111111111111111110000
b-> 00000000000000000000000000001110
a&b-> 00000000000000000000000000000000
sum-> 11111111111111111111111111111110
carry-> 0 输出sum=-2
6.Java位运算符
(1)"~(按位取反)"
~(-14) == 13(int类型) -14(原码):10000000 00000000 00000000 00001110 -14(反码):11111111 11111111 11111111 11110001 -14(补码):11111111 11111111 11111111 11110010 非过程(同时为1才为1):00000000 00000000 00000000 00001101 十进制表示为:1+4+8=13
(2)"&(按位与)"
5&-4 == 4(int类型)
-4(原码):10000000 00000000 00000000 00000100
-4(反码):11111111 11111111 11111111 11111011
-4(补码):11111111 11111111 11111111 11111100
5 : 00000000 00000000 00000000 00000101 -4: 11111111 11111111 11111111 11111100 与过程(同时为1才为1): 00000000 00000000 00000000 00000100 十进制表示为:4
(3)"|(按位或)"
3|6 == 7(int类型) 3: 00000000 00000000 00000000 00000011 6: 00000000 00000000 00000000 00000110 或过程(只要有1就为1): 00000000 00000000 00000000 00000111 十进制表示为:1+2+4=7
(4)"^(按位异或)"
10^3 == 9(int类型) 3 : 00000000 00000000 00000000 00000011 10: 00000000 00000000 00000000 00001010 异或过程(不同为1相同为0): 00000000 00000000 00000000 00001001 十进制表示为:1+8=9
(5)"<<(左移,低位添0补齐)"
-2<<3 == -16(int类型) -2 : 11111111 11111111 11111111 11111110 <<过程:111(舍弃) 11111111 11111111 11111111 11110 000(补零) 十进制表示为:-16
(6)">>(右移,高位添符号位)"
15>>2 == 3(int类型) 15 : (符号位是0)00000000 00000000 00000000 00001111 >>过程:00(补两个0) 000000 00000000 00000000 00000011 11(舍弃最末位两个11) 十进制表示为:1+2=3
(7)">>>(右移,高位添0补齐)"
4>>>2 == 1(int类型) 4 : 00000000 00000000 00000000 00000100 >>>过程:00(补两个0) 000000 00000000 00000000 00000001 00(舍弃最末位两个00) 十进制表示为:1
儿女情长什么的,最影响我们闯荡江湖了。